From c0f45771b0b4d7d60918ae03aca9f14162ff3218 Mon Sep 17 00:00:00 2001 From: Pa1NarK <69745008+pixincreate@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:15:39 +0530 Subject: [PATCH 1/7] feat(cypress): add corner cases (#5481) --- .../e2e/PaymentTest/00020-Variations.cy.js | 325 ++++++++++++++++++ .../cypress/e2e/PaymentUtils/Adyen.js | 21 ++ .../cypress/e2e/PaymentUtils/BankOfAmerica.js | 24 ++ .../cypress/e2e/PaymentUtils/Bluesnap.js | 11 + .../cypress/e2e/PaymentUtils/Commons.js | 181 ++++++++++ .../cypress/e2e/PaymentUtils/Cybersource.js | 24 ++ .../cypress/e2e/PaymentUtils/Datatrans.js | 226 ++++++------ cypress-tests/cypress/e2e/PaymentUtils/Nmi.js | 11 + .../cypress/e2e/PaymentUtils/Paypal.js | 11 + .../cypress/e2e/PaymentUtils/Stripe.js | 24 ++ .../cypress/e2e/PaymentUtils/Trustpay.js | 10 + .../cypress/e2e/PaymentUtils/Utils.js | 11 +- .../cypress/fixtures/confirm-body.json | 2 - 13 files changed, 773 insertions(+), 108 deletions(-) create mode 100644 cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js diff --git a/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js b/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js new file mode 100644 index 000000000000..b413f0d0d0fd --- /dev/null +++ b/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js @@ -0,0 +1,325 @@ +import * as fixtures from "../../fixtures/imports"; +import State from "../../utils/State"; +import getConnectorDetails, * as utils from "../PaymentUtils/Utils"; + +let globalState; +let paymentIntentBody; +let paymentCreateConfirmBody; + +describe("Corner cases", () => { + // This is needed to get flush out old data + beforeEach("seed global state", () => { + paymentIntentBody = Cypress._.cloneDeep(fixtures.createPaymentBody); + paymentCreateConfirmBody = Cypress._.cloneDeep( + fixtures.createConfirmPaymentBody + ); + }); + + context("[Payment] [Payment create] Invalid Card Info", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("[Payment create] Invalid card number", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "invalidCardNumber" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentIntentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + }); + + it("[Payment create] Invalid expiry month", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "invalidExpiryMonth" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentIntentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + }); + + it("[Payment create] Invalid expiry year", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "invalidExpiryYear" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentIntentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + }); + + it("[Payment create] Invalid card CVV", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "invalidCardCvv" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentIntentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + }); + }); + + context("[Payment] Confirm w/o PMD", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("Create payment intent", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "PaymentIntent" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createPaymentIntentTest( + paymentIntentBody, + req_data, + res_data, + "no_three_ds", + "automatic", + globalState + ); + }); + + it("Confirm payment intent", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "PaymentIntentErrored" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.confirmCallTest( + fixtures.confirmBody, + req_data, + res_data, + true, + globalState + ); + }); + }); + + context("[Payment] Capture greater amount", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + it("Create payment intent", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "No3DSManualCapture" + ]; + + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentCreateConfirmBody, + req_data, + res_data, + "no_three_ds", + "manual", + globalState + ); + + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Capture call", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "CaptureGreaterAmount" + ]; + + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.captureCallTest( + fixtures.captureBody, + req_data, + res_data, + 65000, + globalState + ); + + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + }); + + context("[Payment] Capture successful payment", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + it("Create payment intent", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "No3DSAutoCapture" + ]; + + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentCreateConfirmBody, + req_data, + res_data, + "no_three_ds", + "automatic", + globalState + ); + + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Retrieve payment", () => { + cy.retrievePaymentCallTest(globalState); + }); + + it("Capture call", () => { + let data = getConnectorDetails(globalState.get("commons"))["card_pm"][ + "CaptureCapturedAmount" + ]; + + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.captureCallTest( + fixtures.captureBody, + req_data, + res_data, + 65000, + globalState + ); + + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + }); + + context("[Payment] Void successful payment", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + it("Create payment intent", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "No3DSAutoCapture" + ]; + + let req_data = data["Request"]; + let res_data = data["Response"]; + + cy.createConfirmPaymentTest( + paymentCreateConfirmBody, + req_data, + res_data, + "no_three_ds", + "automatic", + globalState + ); + + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Retrieve payment", () => { + cy.retrievePaymentCallTest(globalState); + }); + + it("Void call", () => { + let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"][ + "VoidErrored" + ]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.voidCallTest(fixtures.voidBody, req_data, res_data, globalState); + + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js b/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js index 1e2e7e0f3c3b..92c21497b76a 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js @@ -54,6 +54,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -70,6 +71,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -86,6 +88,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -102,6 +105,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -118,6 +122,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -134,6 +139,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -207,6 +213,7 @@ export const connectorDetails = { }, MandateSingleUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -222,6 +229,7 @@ export const connectorDetails = { }, MandateSingleUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -237,6 +245,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -252,6 +261,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -267,6 +277,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -282,6 +293,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -297,6 +309,7 @@ export const connectorDetails = { }, MandateMultiUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -312,6 +325,7 @@ export const connectorDetails = { }, MandateMultiUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -327,6 +341,7 @@ export const connectorDetails = { }, ZeroAuthMandate: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -342,6 +357,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -365,6 +381,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -388,6 +405,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -411,6 +429,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -434,6 +453,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -458,6 +478,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js b/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js index 544a0adec9bb..675f17bbb09b 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js @@ -52,6 +52,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -68,6 +69,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -84,6 +86,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -100,6 +103,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -116,6 +120,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -132,6 +137,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -171,6 +177,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -186,6 +193,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -201,6 +209,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -216,6 +225,7 @@ export const connectorDetails = { }, MandateSingleUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -231,6 +241,7 @@ export const connectorDetails = { }, MandateSingleUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -246,6 +257,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -261,6 +273,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -276,6 +289,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -291,6 +305,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -306,6 +321,7 @@ export const connectorDetails = { }, MandateMultiUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -321,6 +337,7 @@ export const connectorDetails = { }, MandateMultiUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -336,6 +353,7 @@ export const connectorDetails = { }, ZeroAuthMandate: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -351,6 +369,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -374,6 +393,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -397,6 +417,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -420,6 +441,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -443,6 +465,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -467,6 +490,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js b/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js index 7a7e156b0707..c019fb3c0bea 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js @@ -18,6 +18,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -34,6 +35,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -51,6 +53,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -68,6 +71,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -84,6 +88,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -100,6 +105,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -139,6 +145,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -154,6 +161,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -169,6 +177,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -196,6 +205,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -219,6 +229,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Commons.js b/cypress-tests/cypress/e2e/PaymentUtils/Commons.js index 9529665efb16..68c8fa61fc4d 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Commons.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Commons.js @@ -784,6 +784,7 @@ export const connectorDetails = { }), PaymentMethodIdMandateNo3DSAutoCapture: getCustomExchange({ Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -801,6 +802,7 @@ export const connectorDetails = { }), PaymentMethodIdMandateNo3DSManualCapture: getCustomExchange({ Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -818,6 +820,7 @@ export const connectorDetails = { }), PaymentMethodIdMandate3DSAutoCapture: getCustomExchange({ Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -836,6 +839,7 @@ export const connectorDetails = { }), PaymentMethodIdMandate3DSManualCapture: getCustomExchange({ Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -851,6 +855,183 @@ export const connectorDetails = { }, }, }), + invalidCardNumber: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "123456", + card_exp_month: "10", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: "Json deserialize error: invalid card number length", + }, + }, + }, + invalidExpiryMonth: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "00", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Invalid Expiry Month", + code: "IR_16", + }, + }, + }, + }, + invalidExpiryYear: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Invalid Expiry Year", + code: "IR_16", + }, + }, + }, + }, + invalidCardCvv: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123456", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Invalid card_cvc length", + code: "IR_16", + }, + }, + }, + }, + PaymentIntentErrored: { + Request: { + currency: "USD", + }, + Response: { + status: 422, + body: { + error: { + type: "invalid_request", + message: "A payment token or payment method data is required", + code: "IR_06", + }, + }, + }, + }, + CaptureGreaterAmount: { + Request: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "amount_to_capture is greater than amount", + code: "IR_06", + }, + }, + }, + }, + CaptureCapturedAmount: { + Request: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be captured because it has a payment.status of succeeded. The expected state is requires_capture, partially_captured_and_capturable, processing", + code: "IR_14", + }, + }, + }, + }, + VoidErrored: getCustomExchange({ + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "You cannot cancel this payment because it has status succeeded", + code: "IR_16", + }, + }, + }, + }), }, upi_pm: { PaymentIntent: getCustomExchange({ diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js b/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js index 544a0adec9bb..675f17bbb09b 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js @@ -52,6 +52,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -68,6 +69,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -84,6 +86,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -100,6 +103,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -116,6 +120,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -132,6 +137,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -171,6 +177,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -186,6 +193,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -201,6 +209,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -216,6 +225,7 @@ export const connectorDetails = { }, MandateSingleUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -231,6 +241,7 @@ export const connectorDetails = { }, MandateSingleUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -246,6 +257,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -261,6 +273,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -276,6 +289,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -291,6 +305,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -306,6 +321,7 @@ export const connectorDetails = { }, MandateMultiUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -321,6 +337,7 @@ export const connectorDetails = { }, MandateMultiUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -336,6 +353,7 @@ export const connectorDetails = { }, ZeroAuthMandate: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -351,6 +369,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -374,6 +393,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -397,6 +417,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -420,6 +441,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -443,6 +465,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -467,6 +490,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Datatrans.js b/cypress-tests/cypress/e2e/PaymentUtils/Datatrans.js index bec505e55c3c..ccca47c82afa 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Datatrans.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Datatrans.js @@ -1,131 +1,151 @@ const successfulNo3DSCardDetails = { - card_number: "4444090101010103", - card_exp_month: "06", - card_exp_year: "25", - card_holder_name: "joseph Doe", - card_cvc: "123", - }; - - export const connectorDetails = { - card_pm: { - PaymentIntent: { - Request: { + card_number: "4444090101010103", + card_exp_month: "06", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", +}; + +export const connectorDetails = { + card_pm: { + PaymentIntent: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, - setup_future_usage: "on_session", }, - Response: { - status: 200, - body: { - status: "requires_payment_method", - }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", }, }, - No3DSManualCapture: { - Request: { + }, + No3DSManualCapture: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, - setup_future_usage: "on_session", }, - Response: { - status: 200, - body: { - status: "requires_capture", - }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_capture", }, }, - No3DSAutoCapture: { - Request: { + }, + No3DSAutoCapture: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, - setup_future_usage: "on_session", }, - Response: { - status: 200, - body: { - status: "succeeded", - }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "succeeded", }, }, - Capture: { - Request: { + }, + Capture: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, - }, - Response: { - status: 200, - body: { - status: "succeeded", - amount: 6500, - amount_capturable: 0, - amount_received: 6500, - }, - }, - }, - PartialCapture: { - Request: {}, - Response: { - status: 200, - body: { - status: "partially_captured", - amount: 6500, - amount_capturable: 0, - amount_received: 100, - }, - }, - }, - Void: { - Request: {}, - Response: { - status: 200, - body: { - status: "cancelled", - }, - }, - }, - Refund: { - Request: { + }, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + amount: 6500, + amount_capturable: 0, + amount_received: 6500, + }, + }, + }, + PartialCapture: { + Request: {}, + Response: { + status: 200, + body: { + status: "partially_captured", + amount: 6500, + amount_capturable: 0, + amount_received: 100, + }, + }, + }, + Void: { + Request: {}, + Response: { + status: 200, + body: { + status: "cancelled", + }, + }, + }, + Refund: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, }, - Response: { - status: 200, - body: { - status: "succeeded", - }, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", }, }, - PartialRefund: { - Request: { + }, + PartialRefund: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, }, - Response: { - status: 200, - body: { - status: "succeeded", - }, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", }, }, - SyncRefund: { - Request: { + }, + SyncRefund: { + Request: { + payment_method: "card", + payment_method_data: { card: successfulNo3DSCardDetails, - currency: "USD", - customer_acceptance: null, }, - Response: { - status: 200, - body: { - status: "succeeded", - }, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", }, }, }, + }, }; - \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js b/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js index a5440c4ec880..e5f3304459a4 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js @@ -18,6 +18,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -34,6 +35,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -50,6 +52,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -66,6 +69,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -82,6 +86,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -98,6 +103,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -140,6 +146,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -155,6 +162,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -170,6 +178,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -197,6 +206,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -220,6 +230,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js b/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js index 256606c29c9d..0471a742715d 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js @@ -20,6 +20,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -36,6 +37,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -53,6 +55,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -70,6 +73,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -86,6 +90,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -102,6 +107,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -141,6 +147,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -156,6 +163,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -171,6 +179,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -198,6 +207,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -221,6 +231,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js b/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js index f2cf89df243c..ddbf47f21031 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js @@ -54,6 +54,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -70,6 +71,7 @@ export const connectorDetails = { }, "3DSManualCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -86,6 +88,7 @@ export const connectorDetails = { }, "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -102,6 +105,7 @@ export const connectorDetails = { }, No3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -118,6 +122,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -134,6 +139,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -173,6 +179,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -188,6 +195,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -203,6 +211,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -218,6 +227,7 @@ export const connectorDetails = { }, MandateSingleUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -233,6 +243,7 @@ export const connectorDetails = { }, MandateSingleUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -248,6 +259,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -263,6 +275,7 @@ export const connectorDetails = { }, MandateSingleUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -278,6 +291,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -293,6 +307,7 @@ export const connectorDetails = { }, MandateMultiUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -308,6 +323,7 @@ export const connectorDetails = { }, MandateMultiUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -323,6 +339,7 @@ export const connectorDetails = { }, MandateMultiUse3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -338,6 +355,7 @@ export const connectorDetails = { }, ZeroAuthMandate: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -353,6 +371,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -376,6 +395,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -399,6 +419,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -422,6 +443,7 @@ export const connectorDetails = { }, PaymentMethodIdMandateNo3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -445,6 +467,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -469,6 +492,7 @@ export const connectorDetails = { }, PaymentMethodIdMandate3DSManualCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js b/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js index dae0b3a5deea..31a1d76a89c9 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js @@ -37,6 +37,7 @@ export const connectorDetails = { card_pm: { PaymentIntent: getCustomExchange({ Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -53,6 +54,7 @@ export const connectorDetails = { }), "3DSAutoCapture": { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -69,6 +71,7 @@ export const connectorDetails = { }, No3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -85,6 +88,7 @@ export const connectorDetails = { }, Capture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -105,6 +109,7 @@ export const connectorDetails = { }, PartialCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -138,6 +143,7 @@ export const connectorDetails = { }, Refund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -153,6 +159,7 @@ export const connectorDetails = { }, PartialRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -169,6 +176,7 @@ export const connectorDetails = { }, SyncRefund: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, @@ -184,6 +192,7 @@ export const connectorDetails = { }, MandateMultiUse3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulThreeDSTestCardDetails, }, @@ -211,6 +220,7 @@ export const connectorDetails = { }, SaveCardUseNo3DSAutoCapture: { Request: { + payment_method: "card", payment_method_data: { card: successfulNo3DSCardDetails, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js index 27e0b2b937e9..b22f5de535e7 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js @@ -6,13 +6,13 @@ import { updateDefaultStatusCode, } from "./Commons.js"; import { connectorDetails as cybersourceConnectorDetails } from "./Cybersource.js"; +import { connectorDetails as datatransConnectorDetails } from "./Datatrans.js"; import { connectorDetails as iatapayConnectorDetails } from "./Iatapay.js"; import { connectorDetails as itaubankConnectorDetails } from "./ItauBank.js"; import { connectorDetails as nmiConnectorDetails } from "./Nmi.js"; import { connectorDetails as paypalConnectorDetails } from "./Paypal.js"; import { connectorDetails as stripeConnectorDetails } from "./Stripe.js"; import { connectorDetails as trustpayConnectorDetails } from "./Trustpay.js"; -import { connectorDetails as datatransConnectorDetails } from "./Datatrans.js"; const connectorDetails = { adyen: adyenConnectorDetails, @@ -110,7 +110,12 @@ export function defaultErrorHandler(response, response_data) { } expect(response.body).to.have.property("error"); - for (const key in response_data.body.error) { - expect(response_data.body.error[key]).to.equal(response.body.error[key]); + + if (typeof response.body.error === "object") { + for (const key in response_data.body.error) { + expect(response_data.body.error[key]).to.equal(response.body.error[key]); + } + } else if (typeof response.body.error === "string") { + expect(response.body.error).to.include(response_data.body.error); } } diff --git a/cypress-tests/cypress/fixtures/confirm-body.json b/cypress-tests/cypress/fixtures/confirm-body.json index fbf23a2016b6..fa4769b627f4 100644 --- a/cypress-tests/cypress/fixtures/confirm-body.json +++ b/cypress-tests/cypress/fixtures/confirm-body.json @@ -2,8 +2,6 @@ "client_secret": "", "return_url": "https://hyperswitch.io", "confirm": true, - "payment_method": "card", - "payment_method_data": {}, "customer_acceptance": { "acceptance_type": "offline", "accepted_at": "1963-05-03T04:07:52.723Z", From c036fd7f41a21eb481859671db672b0bcebdca97 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit <64925866+apoorvdixit88@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:26:47 +0530 Subject: [PATCH 2/7] refactor(role): determine level of role entity (#5488) --- crates/api_models/src/user_role/role.rs | 3 ++- crates/common_enums/src/enums.rs | 21 +++++++++++++++++++ crates/diesel_models/src/role.rs | 2 ++ crates/diesel_models/src/schema.rs | 2 ++ crates/diesel_models/src/schema_v2.rs | 2 ++ crates/diesel_models/src/user_role.rs | 5 +++-- crates/router/src/core/user_role/role.rs | 1 + crates/router/src/db/role.rs | 1 + .../src/services/authorization/roles.rs | 8 ++++++- .../authorization/roles/predefined_roles.rs | 11 +++++++++- .../down.sql | 2 ++ .../up.sql | 2 ++ 12 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 migrations/2024-07-30-124102_add_entity_type_to_roles/down.sql create mode 100644 migrations/2024-07-30-124102_add_entity_type_to_roles/up.sql diff --git a/crates/api_models/src/user_role/role.rs b/crates/api_models/src/user_role/role.rs index 6d0bb2154d94..73b25c84af51 100644 --- a/crates/api_models/src/user_role/role.rs +++ b/crates/api_models/src/user_role/role.rs @@ -1,4 +1,4 @@ -use common_enums::{PermissionGroup, RoleScope}; +use common_enums::{EntityType, PermissionGroup, RoleScope}; use super::Permission; @@ -7,6 +7,7 @@ pub struct CreateRoleRequest { pub role_name: String, pub groups: Vec, pub role_scope: RoleScope, + pub entity_type: Option, } #[derive(Debug, serde::Deserialize, serde::Serialize)] diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 4065956a9c00..985f7451c285 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3027,6 +3027,27 @@ pub enum Owner { Internal, } +#[derive( + Clone, + Copy, + Debug, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumString, +)] +#[router_derive::diesel_enum(storage_type = "text")] +#[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum EntityType { + Internal, + Organization, + Merchant, + Profile, +} + #[derive(Clone, Debug, serde::Serialize)] #[serde(rename_all = "snake_case")] pub enum PayoutRetryType { diff --git a/crates/diesel_models/src/role.rs b/crates/diesel_models/src/role.rs index 713590adeda9..3fb64e645d60 100644 --- a/crates/diesel_models/src/role.rs +++ b/crates/diesel_models/src/role.rs @@ -18,6 +18,7 @@ pub struct Role { pub created_by: String, pub last_modified_at: PrimitiveDateTime, pub last_modified_by: String, + pub entity_type: Option, } #[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)] @@ -34,6 +35,7 @@ pub struct RoleNew { pub created_by: String, pub last_modified_at: PrimitiveDateTime, pub last_modified_by: String, + pub entity_type: Option, } #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 37ba11e4389d..d2c66fc37fe3 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1183,6 +1183,8 @@ diesel::table! { last_modified_at -> Timestamp, #[max_length = 64] last_modified_by -> Varchar, + #[max_length = 64] + entity_type -> Nullable, } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 5854fa5f385b..83a444861606 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -1189,6 +1189,8 @@ diesel::table! { last_modified_at -> Timestamp, #[max_length = 64] last_modified_by -> Varchar, + #[max_length = 64] + entity_type -> Nullable, } } diff --git a/crates/diesel_models/src/user_role.rs b/crates/diesel_models/src/user_role.rs index d484db2c6ba1..87e6f3323281 100644 --- a/crates/diesel_models/src/user_role.rs +++ b/crates/diesel_models/src/user_role.rs @@ -1,3 +1,4 @@ +use common_enums::EntityType; use common_utils::id_type; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; @@ -19,7 +20,7 @@ pub struct UserRole { pub last_modified: PrimitiveDateTime, pub profile_id: Option, pub entity_id: Option, - pub entity_type: Option, + pub entity_type: Option, pub version: enums::UserRoleVersion, } @@ -37,7 +38,7 @@ pub struct UserRoleNew { pub last_modified: PrimitiveDateTime, pub profile_id: Option, pub entity_id: Option, - pub entity_type: Option, + pub entity_type: Option, pub version: enums::UserRoleVersion, } diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs index 386156f58e74..a286180e202f 100644 --- a/crates/router/src/core/user_role/role.rs +++ b/crates/router/src/core/user_role/role.rs @@ -87,6 +87,7 @@ pub async fn create_role( org_id: user_from_token.org_id, groups: req.groups, scope: req.role_scope, + entity_type: req.entity_type, created_by: user_from_token.user_id.clone(), last_modified_by: user_from_token.user_id, created_at: now, diff --git a/crates/router/src/db/role.rs b/crates/router/src/db/role.rs index 286a85190b85..5802142b166c 100644 --- a/crates/router/src/db/role.rs +++ b/crates/router/src/db/role.rs @@ -144,6 +144,7 @@ impl RoleInterface for MockDb { org_id: role.org_id, groups: role.groups, scope: role.scope, + entity_type: role.entity_type, created_by: role.created_by, created_at: role.created_at, last_modified_at: role.last_modified_at, diff --git a/crates/router/src/services/authorization/roles.rs b/crates/router/src/services/authorization/roles.rs index cda2371282a7..9b506cc5ef7f 100644 --- a/crates/router/src/services/authorization/roles.rs +++ b/crates/router/src/services/authorization/roles.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use common_enums::{PermissionGroup, RoleScope}; +use common_enums::{EntityType, PermissionGroup, RoleScope}; use common_utils::{errors::CustomResult, id_type}; use super::{permission_groups::get_permissions_vec, permissions::Permission}; @@ -14,6 +14,7 @@ pub struct RoleInfo { role_name: String, groups: Vec, scope: RoleScope, + entity_type: EntityType, is_invitable: bool, is_deletable: bool, is_updatable: bool, @@ -37,6 +38,10 @@ impl RoleInfo { self.scope } + pub fn get_entity_type(&self) -> EntityType { + self.entity_type + } + pub fn is_invitable(&self) -> bool { self.is_invitable } @@ -91,6 +96,7 @@ impl From for RoleInfo { role_name: role.role_name, groups: role.groups.into_iter().map(Into::into).collect(), scope: role.scope, + entity_type: role.entity_type.unwrap_or(EntityType::Merchant), is_invitable: true, is_deletable: true, is_updatable: true, diff --git a/crates/router/src/services/authorization/roles/predefined_roles.rs b/crates/router/src/services/authorization/roles/predefined_roles.rs index 568af6e19fd3..2f67af9f23ad 100644 --- a/crates/router/src/services/authorization/roles/predefined_roles.rs +++ b/crates/router/src/services/authorization/roles/predefined_roles.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use common_enums::{PermissionGroup, RoleScope}; +use common_enums::{EntityType, PermissionGroup, RoleScope}; use once_cell::sync::Lazy; use super::RoleInfo; @@ -28,6 +28,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_INTERNAL_ADMIN.to_string(), role_name: "internal_admin".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Internal, is_invitable: false, is_deletable: false, is_updatable: false, @@ -48,6 +49,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), role_name: "internal_view_only".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Internal, is_invitable: false, is_deletable: false, is_updatable: false, @@ -75,6 +77,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), role_name: "organization_admin".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Organization, is_invitable: false, is_deletable: false, is_updatable: false, @@ -102,6 +105,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_MERCHANT_ADMIN.to_string(), role_name: "admin".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Merchant, is_invitable: true, is_deletable: true, is_updatable: true, @@ -122,6 +126,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_MERCHANT_VIEW_ONLY.to_string(), role_name: "view_only".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Merchant, is_invitable: true, is_deletable: true, is_updatable: true, @@ -141,6 +146,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_MERCHANT_IAM_ADMIN.to_string(), role_name: "iam".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Merchant, is_invitable: true, is_deletable: true, is_updatable: true, @@ -161,6 +167,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_MERCHANT_DEVELOPER.to_string(), role_name: "developer".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Merchant, is_invitable: true, is_deletable: true, is_updatable: true, @@ -182,6 +189,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_MERCHANT_OPERATOR.to_string(), role_name: "operator".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Merchant, is_invitable: true, is_deletable: true, is_updatable: true, @@ -200,6 +208,7 @@ pub static PREDEFINED_ROLES: Lazy> = Lazy::new(| role_id: consts::user_role::ROLE_ID_MERCHANT_CUSTOMER_SUPPORT.to_string(), role_name: "customer_support".to_string(), scope: RoleScope::Organization, + entity_type: EntityType::Merchant, is_invitable: true, is_deletable: true, is_updatable: true, diff --git a/migrations/2024-07-30-124102_add_entity_type_to_roles/down.sql b/migrations/2024-07-30-124102_add_entity_type_to_roles/down.sql new file mode 100644 index 000000000000..af37d90893eb --- /dev/null +++ b/migrations/2024-07-30-124102_add_entity_type_to_roles/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE roles DROP COLUMN entity_type; \ No newline at end of file diff --git a/migrations/2024-07-30-124102_add_entity_type_to_roles/up.sql b/migrations/2024-07-30-124102_add_entity_type_to_roles/up.sql new file mode 100644 index 000000000000..14e0f9450339 --- /dev/null +++ b/migrations/2024-07-30-124102_add_entity_type_to_roles/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE roles ADD COLUMN entity_type VARCHAR(64); \ No newline at end of file From b4e77170559d5912758f18d2db46bf25eb5277b2 Mon Sep 17 00:00:00 2001 From: Kashif Date: Thu, 1 Aug 2024 16:34:31 +0530 Subject: [PATCH 3/7] fix(open_payment_links): send displaySavedPaymentMethods as false explicitly for open payment links (#5501) --- .../payment_link/payment_link_initiate/payment_link_initiator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js index 6269b3546afe..71f0875f42e6 100644 --- a/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js +++ b/crates/router/src/core/payment_link/payment_link_initiate/payment_link_initiator.js @@ -41,6 +41,7 @@ function initializeSDK() { var unifiedCheckoutOptions = { displaySavedPaymentMethodsCheckbox: false, + displaySavedPaymentMethods: false, layout: { type: type, //accordion , tabs, spaced accordion spacedAccordionItems: paymentDetails.sdk_layout === "spaced_accordion", From 85209d12ae3439b555983d62b2cc3bf764c1b441 Mon Sep 17 00:00:00 2001 From: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:27:39 +0530 Subject: [PATCH 4/7] refactor(router): domain and diesel model changes for merchant_connector_account create v2 flow (#5462) --- crates/api_models/src/admin.rs | 102 ++- crates/diesel_models/Cargo.toml | 1 + .../src/merchant_connector_account.rs | 171 +++++- .../src/query/merchant_connector_account.rs | 81 ++- crates/diesel_models/src/schema_v2.rs | 14 +- crates/hyperswitch_domain_models/Cargo.toml | 1 + crates/hyperswitch_domain_models/src/lib.rs | 1 + .../src/merchant_connector_account.rs | 501 +++++++++++++++ crates/kgraph_utils/Cargo.toml | 2 +- crates/kgraph_utils/benches/evaluation.rs | 2 +- crates/kgraph_utils/src/mca.rs | 2 +- crates/router/Cargo.toml | 4 +- crates/router/src/core/admin.rs | 110 +++- crates/router/src/core/fraud_check.rs | 14 + .../router/src/core/payment_methods/cards.rs | 20 +- .../router/src/core/payment_methods/utils.rs | 6 +- crates/router/src/core/payments/helpers.rs | 104 +++- .../payments/operations/payment_session.rs | 25 +- crates/router/src/core/payments/routing.rs | 4 +- crates/router/src/core/payout_link.rs | 4 +- crates/router/src/core/pm_auth.rs | 35 +- crates/router/src/core/refunds.rs | 4 +- crates/router/src/core/routing/helpers.rs | 4 +- .../src/core/user/dashboard_metadata.rs | 46 +- crates/router/src/core/verification.rs | 11 + crates/router/src/core/verification/utils.rs | 31 + crates/router/src/core/webhooks/incoming.rs | 19 +- crates/router/src/db/kafka_store.rs | 42 ++ .../src/db/merchant_connector_account.rs | 581 +++++++++++++++++- crates/router/src/types/api.rs | 14 + .../domain/merchant_connector_account.rs | 257 +------- crates/router/src/types/transformers.rs | 72 ++- crates/router/src/utils.rs | 234 ++++--- .../router/src/utils/connector_onboarding.rs | 11 + .../2024-07-31-065721_mca_v2/down.sql | 21 + v2_migrations/2024-07-31-065721_mca_v2/up.sql | 26 + 36 files changed, 2105 insertions(+), 472 deletions(-) create mode 100644 crates/hyperswitch_domain_models/src/merchant_connector_account.rs create mode 100644 v2_migrations/2024-07-31-065721_mca_v2/down.sql create mode 100644 v2_migrations/2024-07-31-065721_mca_v2/up.sql diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index e7c1058b5b32..e5da8754557a 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -951,7 +951,7 @@ pub struct MerchantConnectorResponse { /// Unique ID of the merchant connector account #[schema(example = "mca_5apGeP94tMts6rg3U3kR")] - pub connector_id: String, + pub id: String, /// Identifier for the business profile, if not provided default will be chosen from merchant account #[schema(max_length = 64)] @@ -1032,7 +1032,7 @@ impl MerchantConnectorResponse { pub fn to_merchant_connector_info(&self, connector_label: &String) -> MerchantConnectorInfo { MerchantConnectorInfo { connector_label: connector_label.to_string(), - merchant_connector_id: self.connector_id.clone(), + merchant_connector_id: self.id.clone(), } } } @@ -1162,6 +1162,10 @@ impl MerchantConnectorResponse { } } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(deny_unknown_fields)] pub struct MerchantConnectorListResponse { @@ -1257,6 +1261,10 @@ pub struct MerchantConnectorListResponse { pub additional_merchant_data: Option, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] impl MerchantConnectorListResponse { pub fn to_merchant_connector_info(&self, connector_label: &String) -> MerchantConnectorInfo { MerchantConnectorInfo { @@ -1266,6 +1274,96 @@ impl MerchantConnectorListResponse { } } +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(deny_unknown_fields)] +pub struct MerchantConnectorListResponse { + /// Type of the Connector for the financial use case. Could range from Payments to Accounting to Banking. + #[schema(value_type = ConnectorType, example = "payment_processor")] + pub connector_type: api_enums::ConnectorType, + /// Name of the Connector + #[schema(value_type = Connector, example = "stripe")] + pub connector_name: String, + + /// A unique label to identify the connector account created under a business profile + #[schema(example = "stripe_US_travel")] + pub connector_label: Option, + + /// Unique ID of the merchant connector account + #[schema(example = "mca_5apGeP94tMts6rg3U3kR")] + pub id: String, + + /// Identifier for the business profile, if not provided default will be chosen from merchant account + #[schema(max_length = 64)] + pub profile_id: Option, + + /// An object containing the details about the payment methods that need to be enabled under this merchant connector account + #[schema(example = json!([ + { + "payment_method": "wallet", + "payment_method_types": [ + "upi_collect", + "upi_intent" + ], + "payment_method_issuers": [ + "labore magna ipsum", + "aute" + ], + "payment_schemes": [ + "Discover", + "Discover" + ], + "accepted_currencies": { + "type": "enable_only", + "list": ["USD", "EUR"] + }, + "accepted_countries": { + "type": "disable_only", + "list": ["FR", "DE","IN"] + }, + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + } + ]))] + pub payment_methods_enabled: Option>, + + /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object. + #[schema(value_type = Option,max_length = 255,example = json!({ "city": "NY", "unit": "245" }))] + pub metadata: Option, + + /// A boolean value to indicate if the connector is disabled. By default, its value is false. + #[schema(default = false, example = false)] + pub disabled: Option, + + /// Contains the frm configs for the merchant connector + #[schema(example = json!(consts::FRM_CONFIGS_EG))] + pub frm_configs: Option>, + + /// identifier for the verified domains of a particular connector account + pub applepay_verified_domains: Option>, + + #[schema(value_type = Option)] + pub pm_auth_config: Option, + + #[schema(value_type = ConnectorStatus, example = "inactive")] + pub status: api_enums::ConnectorStatus, + + #[schema(value_type = Option)] + pub additional_merchant_data: Option, +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +impl MerchantConnectorListResponse { + pub fn to_merchant_connector_info(&self, connector_label: &String) -> MerchantConnectorInfo { + MerchantConnectorInfo { + connector_label: connector_label.to_string(), + merchant_connector_id: self.id.clone(), + } + } +} + /// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc." #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(deny_unknown_fields)] diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index c596cb2e2e17..d021ccfff373 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -14,6 +14,7 @@ v1 =[] v2 = [] customer_v2 = [] merchant_account_v2 = [] +merchant_connector_account_v2 = [] payment_v2 = [] [dependencies] diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index 2d12dc9f25cd..a057f349ead5 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -2,10 +2,20 @@ use std::fmt::Debug; use common_utils::{encryption::Encryption, id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; -use masking::Secret; -use crate::{enums as storage_enums, schema::merchant_connector_account}; +use crate::enums as storage_enums; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +use crate::schema::merchant_connector_account; +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +use crate::schema_v2::merchant_connector_account; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] #[derive( Clone, Debug, @@ -24,20 +34,20 @@ pub struct MerchantConnectorAccount { pub test_mode: Option, pub disabled: Option, pub merchant_connector_id: String, - #[diesel(deserialize_as = super::OptionalDieselArray)] - pub payment_methods_enabled: Option>, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub payment_methods_enabled: Option>, pub connector_type: storage_enums::ConnectorType, pub metadata: Option, pub connector_label: Option, pub business_country: Option, pub business_label: Option, pub business_sub_label: Option, - pub frm_configs: Option>, + pub frm_configs: Option, pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, pub connector_webhook_details: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] - pub frm_config: Option>>, + pub frm_config: Option>, pub profile_id: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, @@ -47,6 +57,64 @@ pub struct MerchantConnectorAccount { pub connector_wallets_details: Option, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +impl MerchantConnectorAccount { + pub fn get_id(&self) -> String { + self.merchant_connector_id.clone() + } +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + Identifiable, + Queryable, + Selectable, + router_derive::DebugAsDisplay, +)] +#[diesel(table_name = merchant_connector_account, check_for_backend(diesel::pg::Pg))] +pub struct MerchantConnectorAccount { + pub merchant_id: id_type::MerchantId, + pub connector_name: String, + pub connector_account_details: Encryption, + pub disabled: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub payment_methods_enabled: Option>, + pub connector_type: storage_enums::ConnectorType, + pub metadata: Option, + pub connector_label: Option, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub connector_webhook_details: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub frm_config: Option>, + pub profile_id: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, + pub status: storage_enums::ConnectorStatus, + pub additional_merchant_data: Option, + pub connector_wallets_details: Option, + pub id: String, +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +impl MerchantConnectorAccount { + pub fn get_id(&self) -> String { + self.id.clone() + } +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = merchant_connector_account)] pub struct MerchantConnectorAccountNew { @@ -57,18 +125,44 @@ pub struct MerchantConnectorAccountNew { pub test_mode: Option, pub disabled: Option, pub merchant_connector_id: String, - pub payment_methods_enabled: Option>, + pub payment_methods_enabled: Option>, pub metadata: Option, pub connector_label: Option, pub business_country: Option, pub business_label: Option, pub business_sub_label: Option, - pub frm_configs: Option>, + pub frm_configs: Option, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub connector_webhook_details: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub frm_config: Option>, + pub profile_id: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, + pub status: storage_enums::ConnectorStatus, + pub additional_merchant_data: Option, + pub connector_wallets_details: Option, +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] +#[diesel(table_name = merchant_connector_account)] +pub struct MerchantConnectorAccountNew { + pub merchant_id: Option, + pub connector_type: Option, + pub connector_name: Option, + pub connector_account_details: Option, + pub disabled: Option, + pub payment_methods_enabled: Option>, + pub metadata: Option, + pub connector_label: Option, pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, pub connector_webhook_details: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] - pub frm_config: Option>>, + pub frm_config: Option>, pub profile_id: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, @@ -76,8 +170,13 @@ pub struct MerchantConnectorAccountNew { pub status: storage_enums::ConnectorStatus, pub additional_merchant_data: Option, pub connector_wallets_details: Option, + pub id: String, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = merchant_connector_account)] pub struct MerchantConnectorAccountUpdateInternal { @@ -88,13 +187,13 @@ pub struct MerchantConnectorAccountUpdateInternal { pub test_mode: Option, pub disabled: Option, pub merchant_connector_id: Option, - pub payment_methods_enabled: Option>, - pub frm_configs: Option>, + pub payment_methods_enabled: Option>, + pub frm_configs: Option, pub metadata: Option, pub modified_at: Option, pub connector_webhook_details: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] - pub frm_config: Option>>, + pub frm_config: Option>, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, pub pm_auth_config: Option, @@ -102,6 +201,31 @@ pub struct MerchantConnectorAccountUpdateInternal { pub connector_wallets_details: Option, } +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] +#[diesel(table_name = merchant_connector_account)] +pub struct MerchantConnectorAccountUpdateInternal { + pub connector_type: Option, + pub connector_account_details: Option, + pub connector_label: Option, + pub disabled: Option, + pub payment_methods_enabled: Option>, + pub metadata: Option, + pub modified_at: Option, + pub connector_webhook_details: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub frm_config: Option>, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, + pub status: Option, + pub connector_wallets_details: Option, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] impl MerchantConnectorAccountUpdateInternal { pub fn create_merchant_connector_account( self, @@ -128,3 +252,26 @@ impl MerchantConnectorAccountUpdateInternal { } } } + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +impl MerchantConnectorAccountUpdateInternal { + pub fn create_merchant_connector_account( + self, + source: MerchantConnectorAccount, + ) -> MerchantConnectorAccount { + MerchantConnectorAccount { + connector_type: self.connector_type.unwrap_or(source.connector_type), + connector_account_details: self + .connector_account_details + .unwrap_or(source.connector_account_details), + disabled: self.disabled, + payment_methods_enabled: self.payment_methods_enabled, + frm_config: self.frm_config, + modified_at: self.modified_at.unwrap_or(source.modified_at), + pm_auth_config: self.pm_auth_config, + status: self.status.unwrap_or(source.status), + + ..source + } + } +} diff --git a/crates/diesel_models/src/query/merchant_connector_account.rs b/crates/diesel_models/src/query/merchant_connector_account.rs index ee4cc843465e..e4dcdba59757 100644 --- a/crates/diesel_models/src/query/merchant_connector_account.rs +++ b/crates/diesel_models/src/query/merchant_connector_account.rs @@ -1,13 +1,19 @@ use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Table}; use super::generics; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +use crate::schema::merchant_connector_account::dsl; +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +use crate::schema_v2::merchant_connector_account::dsl; use crate::{ errors, merchant_connector_account::{ MerchantConnectorAccount, MerchantConnectorAccountNew, MerchantConnectorAccountUpdateInternal, }, - schema::merchant_connector_account::dsl, PgPooledConn, StorageResult, }; @@ -17,6 +23,10 @@ impl MerchantConnectorAccountNew { } } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] impl MerchantConnectorAccount { pub async fn update( self, @@ -149,3 +159,72 @@ impl MerchantConnectorAccount { } } } + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +impl MerchantConnectorAccount { + pub async fn update( + self, + conn: &PgPooledConn, + merchant_connector_account: MerchantConnectorAccountUpdateInternal, + ) -> StorageResult { + match generics::generic_update_by_id::<::Table, _, _, _>( + conn, + self.id.to_owned(), + merchant_connector_account, + ) + .await + { + Err(error) => match error.current_context() { + errors::DatabaseError::NoFieldsToUpdate => Ok(self), + _ => Err(error), + }, + result => result, + } + } + + pub async fn delete_by_id(conn: &PgPooledConn, id: &str) -> StorageResult { + generics::generic_delete::<::Table, _>(conn, dsl::id.eq(id.to_owned())) + .await + } + + pub async fn find_by_id(conn: &PgPooledConn, id: &str) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + dsl::id.eq(id.to_owned()), + ) + .await + } + + pub async fn find_by_merchant_id( + conn: &PgPooledConn, + merchant_id: &common_utils::id_type::MerchantId, + get_disabled: bool, + ) -> StorageResult> { + if get_disabled { + generics::generic_filter::<::Table, _, _, _>( + conn, + dsl::merchant_id.eq(merchant_id.to_owned()), + None, + None, + Some(dsl::created_at.asc()), + ) + .await + } else { + generics::generic_filter::< + ::Table, + _, + <::Table as Table>::PrimaryKey, + _, + >( + conn, + dsl::merchant_id + .eq(merchant_id.to_owned()) + .and(dsl::disabled.eq(false)), + None, + None, + None, + ) + .await + } + } +} diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 83a444861606..648e6e44652e 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -676,28 +676,18 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - merchant_connector_account (merchant_connector_id) { - id -> Int4, + merchant_connector_account (id) { #[max_length = 64] merchant_id -> Varchar, #[max_length = 64] connector_name -> Varchar, connector_account_details -> Bytea, - test_mode -> Nullable, disabled -> Nullable, - #[max_length = 128] - merchant_connector_id -> Varchar, payment_methods_enabled -> Nullable>>, connector_type -> ConnectorType, metadata -> Nullable, #[max_length = 255] connector_label -> Nullable, - business_country -> Nullable, - #[max_length = 255] - business_label -> Nullable, - #[max_length = 64] - business_sub_label -> Nullable, - frm_configs -> Nullable, created_at -> Timestamp, modified_at -> Timestamp, connector_webhook_details -> Nullable, @@ -709,6 +699,8 @@ diesel::table! { status -> ConnectorStatus, additional_merchant_data -> Nullable, connector_wallets_details -> Nullable, + #[max_length = 64] + id -> Varchar, } } diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index adbea0effb57..662cfcff65d6 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -17,6 +17,7 @@ v2 = ["api_models/v2", "diesel_models/v2"] v1 = ["api_models/v1", "diesel_models/v1"] customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2"] merchant_account_v2 = ["api_models/merchant_account_v2", "diesel_models/merchant_account_v2"] +merchant_connector_account_v2 = ["api_models/merchant_connector_account_v2", "diesel_models/merchant_connector_account_v2"] payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2"] [dependencies] diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 450fdac8d8c4..e7ad703f9274 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -4,6 +4,7 @@ pub mod customer; pub mod errors; pub mod mandates; pub mod merchant_account; +pub mod merchant_connector_account; pub mod merchant_key_store; pub mod payment_address; pub mod payment_method_data; diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs new file mode 100644 index 000000000000..cea595aed645 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -0,0 +1,501 @@ +use common_utils::{ + crypto::Encryptable, + date_time, + encryption::Encryption, + errors::{CustomResult, ValidationError}, + pii, + types::keymanager::{Identifier, KeyManagerState}, +}; +use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; +use error_stack::ResultExt; +use masking::{PeekInterface, Secret}; + +use super::behaviour; +use crate::type_encryption::{decrypt, decrypt_optional, AsyncLift}; + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +#[derive(Clone, Debug)] +pub struct MerchantConnectorAccount { + pub merchant_id: common_utils::id_type::MerchantId, + pub connector_name: String, + pub connector_account_details: Encryptable, + pub test_mode: Option, + pub disabled: Option, + pub merchant_connector_id: String, + pub payment_methods_enabled: Option>, + pub connector_type: enums::ConnectorType, + pub metadata: Option, + pub frm_configs: Option>, + pub connector_label: Option, + pub business_country: Option, + pub business_label: Option, + pub business_sub_label: Option, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub connector_webhook_details: Option, + pub profile_id: Option, + pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, + pub status: enums::ConnectorStatus, + pub connector_wallets_details: Option>, + pub additional_merchant_data: Option>, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +impl MerchantConnectorAccount { + pub fn get_id(&self) -> String { + self.merchant_connector_id.clone() + } +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[derive(Clone, Debug)] +pub struct MerchantConnectorAccount { + pub id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub connector_name: String, + pub connector_account_details: Encryptable, + pub disabled: Option, + pub payment_methods_enabled: Option>, + pub connector_type: enums::ConnectorType, + pub metadata: Option, + pub frm_configs: Option>, + pub connector_label: Option, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub connector_webhook_details: Option, + pub profile_id: Option, + pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, + pub status: enums::ConnectorStatus, + pub connector_wallets_details: Option>, + pub additional_merchant_data: Option>, +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +impl MerchantConnectorAccount { + pub fn get_id(&self) -> String { + self.id.clone() + } +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +#[derive(Debug)] +pub enum MerchantConnectorAccountUpdate { + Update { + connector_type: Option, + connector_name: Option, + connector_account_details: Option>, + test_mode: Option, + disabled: Option, + merchant_connector_id: Option, + payment_methods_enabled: Option>, + metadata: Option, + frm_configs: Option>, + connector_webhook_details: Option, + applepay_verified_domains: Option>, + pm_auth_config: Option, + connector_label: Option, + status: Option, + connector_wallets_details: Option>, + }, + ConnectorWalletDetailsUpdate { + connector_wallets_details: Encryptable, + }, +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[derive(Debug)] +pub enum MerchantConnectorAccountUpdate { + Update { + connector_type: Option, + connector_account_details: Option>, + disabled: Option, + payment_methods_enabled: Option>, + metadata: Option, + frm_configs: Option>, + connector_webhook_details: Option, + applepay_verified_domains: Option>, + pm_auth_config: Option, + connector_label: Option, + status: Option, + connector_wallets_details: Option>, + }, + ConnectorWalletDetailsUpdate { + connector_wallets_details: Encryptable, + }, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +#[async_trait::async_trait] +impl behaviour::Conversion for MerchantConnectorAccount { + type DstType = diesel_models::merchant_connector_account::MerchantConnectorAccount; + type NewDstType = diesel_models::merchant_connector_account::MerchantConnectorAccountNew; + + async fn convert(self) -> CustomResult { + Ok( + diesel_models::merchant_connector_account::MerchantConnectorAccount { + merchant_id: self.merchant_id, + connector_name: self.connector_name, + connector_account_details: self.connector_account_details.into(), + test_mode: self.test_mode, + disabled: self.disabled, + merchant_connector_id: self.merchant_connector_id, + payment_methods_enabled: self.payment_methods_enabled, + connector_type: self.connector_type, + metadata: self.metadata, + frm_configs: None, + frm_config: self.frm_configs, + business_country: self.business_country, + business_label: self.business_label, + connector_label: self.connector_label, + business_sub_label: self.business_sub_label, + created_at: self.created_at, + modified_at: self.modified_at, + connector_webhook_details: self.connector_webhook_details, + profile_id: self.profile_id, + applepay_verified_domains: self.applepay_verified_domains, + pm_auth_config: self.pm_auth_config, + status: self.status, + connector_wallets_details: self.connector_wallets_details.map(Encryption::from), + additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + }, + ) + } + + async fn convert_back( + state: &KeyManagerState, + other: Self::DstType, + key: &Secret>, + _key_manager_identifier: Identifier, + ) -> CustomResult { + let identifier = Identifier::Merchant(other.merchant_id.clone()); + Ok(Self { + merchant_id: other.merchant_id, + connector_name: other.connector_name, + connector_account_details: decrypt( + state, + other.connector_account_details, + identifier.clone(), + key.peek(), + ) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting connector account details".to_string(), + })?, + test_mode: other.test_mode, + disabled: other.disabled, + merchant_connector_id: other.merchant_connector_id, + payment_methods_enabled: other.payment_methods_enabled, + connector_type: other.connector_type, + metadata: other.metadata, + + frm_configs: other.frm_config, + business_country: other.business_country, + business_label: other.business_label, + connector_label: other.connector_label, + business_sub_label: other.business_sub_label, + created_at: other.created_at, + modified_at: other.modified_at, + connector_webhook_details: other.connector_webhook_details, + profile_id: other.profile_id, + applepay_verified_domains: other.applepay_verified_domains, + pm_auth_config: other.pm_auth_config, + status: other.status, + connector_wallets_details: other + .connector_wallets_details + .async_lift(|inner| decrypt_optional(state, inner, identifier.clone(), key.peek())) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting connector wallets details".to_string(), + })?, + additional_merchant_data: if let Some(data) = other.additional_merchant_data { + Some( + decrypt(state, data, identifier, key.peek()) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting additional_merchant_data".to_string(), + })?, + ) + } else { + None + }, + }) + } + + async fn construct_new(self) -> CustomResult { + let now = date_time::now(); + Ok(Self::NewDstType { + merchant_id: Some(self.merchant_id), + connector_name: Some(self.connector_name), + connector_account_details: Some(self.connector_account_details.into()), + test_mode: self.test_mode, + disabled: self.disabled, + merchant_connector_id: self.merchant_connector_id, + payment_methods_enabled: self.payment_methods_enabled, + connector_type: Some(self.connector_type), + metadata: self.metadata, + frm_configs: None, + frm_config: self.frm_configs, + business_country: self.business_country, + business_label: self.business_label, + connector_label: self.connector_label, + business_sub_label: self.business_sub_label, + created_at: now, + modified_at: now, + connector_webhook_details: self.connector_webhook_details, + profile_id: self.profile_id, + applepay_verified_domains: self.applepay_verified_domains, + pm_auth_config: self.pm_auth_config, + status: self.status, + connector_wallets_details: self.connector_wallets_details.map(Encryption::from), + additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + }) + } +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +#[async_trait::async_trait] +impl behaviour::Conversion for MerchantConnectorAccount { + type DstType = diesel_models::merchant_connector_account::MerchantConnectorAccount; + type NewDstType = diesel_models::merchant_connector_account::MerchantConnectorAccountNew; + + async fn convert(self) -> CustomResult { + Ok( + diesel_models::merchant_connector_account::MerchantConnectorAccount { + id: self.id, + merchant_id: self.merchant_id, + connector_name: self.connector_name, + connector_account_details: self.connector_account_details.into(), + disabled: self.disabled, + payment_methods_enabled: self.payment_methods_enabled, + connector_type: self.connector_type, + metadata: self.metadata, + frm_config: self.frm_configs, + connector_label: self.connector_label, + created_at: self.created_at, + modified_at: self.modified_at, + connector_webhook_details: self.connector_webhook_details, + profile_id: self.profile_id, + applepay_verified_domains: self.applepay_verified_domains, + pm_auth_config: self.pm_auth_config, + status: self.status, + connector_wallets_details: self.connector_wallets_details.map(Encryption::from), + additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + }, + ) + } + + async fn convert_back( + state: &KeyManagerState, + other: Self::DstType, + key: &Secret>, + _key_manager_identifier: Identifier, + ) -> CustomResult { + let identifier = Identifier::Merchant(other.merchant_id.clone()); + Ok(Self { + id: other.id, + merchant_id: other.merchant_id, + connector_name: other.connector_name, + connector_account_details: decrypt( + state, + other.connector_account_details, + identifier.clone(), + key.peek(), + ) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting connector account details".to_string(), + })?, + disabled: other.disabled, + payment_methods_enabled: other.payment_methods_enabled, + connector_type: other.connector_type, + metadata: other.metadata, + + frm_configs: other.frm_config, + connector_label: other.connector_label, + created_at: other.created_at, + modified_at: other.modified_at, + connector_webhook_details: other.connector_webhook_details, + profile_id: other.profile_id, + applepay_verified_domains: other.applepay_verified_domains, + pm_auth_config: other.pm_auth_config, + status: other.status, + connector_wallets_details: other + .connector_wallets_details + .async_lift(|inner| decrypt_optional(state, inner, identifier.clone(), key.peek())) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting connector wallets details".to_string(), + })?, + additional_merchant_data: if let Some(data) = other.additional_merchant_data { + Some( + decrypt(state, data, identifier, key.peek()) + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting additional_merchant_data".to_string(), + })?, + ) + } else { + None + }, + }) + } + + async fn construct_new(self) -> CustomResult { + let now = date_time::now(); + Ok(Self::NewDstType { + id: self.id, + merchant_id: Some(self.merchant_id), + connector_name: Some(self.connector_name), + connector_account_details: Some(self.connector_account_details.into()), + disabled: self.disabled, + payment_methods_enabled: self.payment_methods_enabled, + connector_type: Some(self.connector_type), + metadata: self.metadata, + frm_config: self.frm_configs, + connector_label: self.connector_label, + created_at: now, + modified_at: now, + connector_webhook_details: self.connector_webhook_details, + profile_id: self.profile_id, + applepay_verified_domains: self.applepay_verified_domains, + pm_auth_config: self.pm_auth_config, + status: self.status, + connector_wallets_details: self.connector_wallets_details.map(Encryption::from), + additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + }) + } +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") +))] +impl From for MerchantConnectorAccountUpdateInternal { + fn from(merchant_connector_account_update: MerchantConnectorAccountUpdate) -> Self { + match merchant_connector_account_update { + MerchantConnectorAccountUpdate::Update { + connector_type, + connector_name, + connector_account_details, + test_mode, + disabled, + merchant_connector_id, + payment_methods_enabled, + metadata, + frm_configs, + connector_webhook_details, + applepay_verified_domains, + pm_auth_config, + connector_label, + status, + connector_wallets_details, + } => Self { + connector_type, + connector_name, + connector_account_details: connector_account_details.map(Encryption::from), + test_mode, + disabled, + merchant_connector_id, + payment_methods_enabled, + metadata, + frm_configs: None, + frm_config: frm_configs, + modified_at: Some(date_time::now()), + connector_webhook_details, + applepay_verified_domains, + pm_auth_config, + connector_label, + status, + connector_wallets_details: connector_wallets_details.map(Encryption::from), + }, + MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate { + connector_wallets_details, + } => Self { + connector_wallets_details: Some(Encryption::from(connector_wallets_details)), + connector_type: None, + connector_name: None, + connector_account_details: None, + connector_label: None, + test_mode: None, + disabled: None, + merchant_connector_id: None, + payment_methods_enabled: None, + frm_configs: None, + metadata: None, + modified_at: None, + connector_webhook_details: None, + frm_config: None, + applepay_verified_domains: None, + pm_auth_config: None, + status: None, + }, + } + } +} + +#[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] +impl From for MerchantConnectorAccountUpdateInternal { + fn from(merchant_connector_account_update: MerchantConnectorAccountUpdate) -> Self { + match merchant_connector_account_update { + MerchantConnectorAccountUpdate::Update { + connector_type, + connector_account_details, + disabled, + payment_methods_enabled, + metadata, + frm_configs, + connector_webhook_details, + applepay_verified_domains, + pm_auth_config, + connector_label, + status, + connector_wallets_details, + } => Self { + connector_type, + connector_account_details: connector_account_details.map(Encryption::from), + disabled, + payment_methods_enabled, + metadata, + frm_config: frm_configs, + modified_at: Some(date_time::now()), + connector_webhook_details, + applepay_verified_domains, + pm_auth_config, + connector_label, + status, + connector_wallets_details: connector_wallets_details.map(Encryption::from), + }, + MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate { + connector_wallets_details, + } => Self { + connector_wallets_details: Some(Encryption::from(connector_wallets_details)), + connector_type: None, + connector_account_details: None, + connector_label: None, + disabled: None, + payment_methods_enabled: None, + metadata: None, + modified_at: None, + connector_webhook_details: None, + frm_config: None, + applepay_verified_domains: None, + pm_auth_config: None, + status: None, + }, + } + } +} diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index 7dfebc3604c8..09c0ebd3e3ad 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -7,7 +7,7 @@ rust-version.workspace = true license.workspace = true [features] -default = ["v1"] +default = [] dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector"] v1 = [] v2 = [] diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index a48aefe4c9f7..5fdab57aac0c 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -56,7 +56,7 @@ fn build_test_data( let stripe_account = MerchantConnectorResponse { connector_type: api_enums::ConnectorType::FizOperations, connector_name: "stripe".to_string(), - connector_id: "something".to_string(), + id: "something".to_string(), connector_account_details: masking::Secret::new(serde_json::json!({})), disabled: None, metadata: None, diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 95bcf1a9e01e..2c61bdfe19b4 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -708,7 +708,7 @@ mod tests { let stripe_account = MerchantConnectorResponse { connector_type: api_enums::ConnectorType::FizOperations, connector_name: "stripe".to_string(), - connector_id: "something".to_string(), + id: "something".to_string(), connector_label: Some("something".to_string()), connector_account_details: masking::Secret::new(serde_json::json!({})), disabled: None, diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 7f713b889edd..398e9143cfcf 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -32,12 +32,12 @@ payout_retry = ["payouts"] recon = ["email", "api_models/recon"] retry = [] v2 = ["api_models/v2", "diesel_models/v2", "hyperswitch_domain_models/v2", "storage_impl/v2", "kgraph_utils/v2"] -v1 = ["api_models/v1", "diesel_models/v1", "hyperswitch_domain_models/v1", "storage_impl/v1", "hyperswitch_interfaces/v1"] +v1 = ["api_models/v1", "diesel_models/v1", "hyperswitch_domain_models/v1", "storage_impl/v1", "hyperswitch_interfaces/v1", "kgraph_utils/v1"] customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2"] merchant_account_v2 = ["api_models/merchant_account_v2", "diesel_models/merchant_account_v2", "hyperswitch_domain_models/merchant_account_v2"] payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2"] routing_v2 = ["api_models/routing_v2"] -merchant_connector_account_v2 = ["api_models/merchant_connector_account_v2", "kgraph_utils/merchant_connector_account_v2"] +merchant_connector_account_v2 = ["api_models/merchant_connector_account_v2", "kgraph_utils/merchant_connector_account_v2", "hyperswitch_domain_models/merchant_connector_account_v2", "diesel_models/merchant_connector_account_v2"] # Partial Auth # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request. diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 964c24cc8384..26afafd7ad5e 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -49,7 +49,7 @@ use crate::{ types::{self as domain_types, AsyncLift}, }, storage::{self, enums::MerchantStorageScheme}, - transformers::ForeignTryFrom, + transformers::{ForeignTryFrom, ForeignTryInto}, }, utils, }; @@ -1667,7 +1667,7 @@ struct PaymentMethodsEnabled<'a> { } impl<'a> PaymentMethodsEnabled<'a> { - fn get_payment_methods_enabled(&self) -> RouterResult>> { + fn get_payment_methods_enabled(&self) -> RouterResult>> { let mut vec = Vec::new(); let payment_methods_enabled = match self.payment_methods_enabled.clone() { Some(val) => { @@ -1678,7 +1678,7 @@ impl<'a> PaymentMethodsEnabled<'a> { .attach_printable( "Failed while encoding to serde_json::Value, PaymentMethod", )?; - vec.push(pm_value) + vec.push(Secret::new(pm_value)) } Some(vec) } @@ -1783,7 +1783,7 @@ impl<'a> PMAuthConfigValidation<'a> { let pm_auth_mca = all_mcas .clone() .into_iter() - .find(|mca| mca.merchant_connector_id == conn_choice.mca_id) + .find(|mca| mca.get_id() == conn_choice.mca_id) .ok_or(errors::ApiErrorResponse::GenericNotFoundError { message: "payment method auth connector account not found".to_string(), })?; @@ -1993,7 +1993,6 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { merchant_id: business_profile.merchant_id.clone(), connector_type: self.connector_type, connector_name: self.connector_name.to_string(), - merchant_connector_id: utils::generate_id(consts::ID_LENGTH, "mca"), connector_account_details: domain_types::encrypt( key_manager_state, self.connector_account_details.ok_or( @@ -2014,6 +2013,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { connector_label: Some(connector_label.clone()), created_at: date_time::now(), modified_at: date_time::now(), + id: common_utils::generate_time_ordered_id("mca"), connector_webhook_details: match self.connector_webhook_details { Some(connector_webhook_details) => { connector_webhook_details.encode_to_value( @@ -2030,10 +2030,6 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { pm_auth_config: self.pm_auth_config.clone(), status: connector_status, connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(state, &key_store, &self.metadata).await?, - test_mode: None, - business_country: None, - business_label: None, - business_sub_label: None, additional_merchant_data: if let Some(mcd) = merchant_recipient_data { Some(domain_types::encrypt( key_manager_state, @@ -2399,7 +2395,7 @@ pub async fn create_payment_connector( //update merchant default config let merchant_default_config_update = MerchantDefaultConfigUpdate { routable_connector: &routable_connector, - merchant_connector_id: &mca.merchant_connector_id, + merchant_connector_id: &mca.get_id(), store, merchant_id, default_routing_config: &mut default_routing_config, @@ -2421,7 +2417,7 @@ pub async fn create_payment_connector( ]), ); - let mca_response = mca.try_into()?; + let mca_response = mca.foreign_try_into()?; Ok(service_api::ApplicationResponse::Json(mca_response)) } @@ -2456,7 +2452,7 @@ async fn validate_pm_auth( for conn_choice in config.enabled_payment_methods { let pm_auth_mca = all_mcas .iter() - .find(|mca| mca.merchant_connector_id == conn_choice.mca_id) + .find(|mca| mca.get_id() == conn_choice.mca_id) .ok_or(errors::ApiErrorResponse::GenericNotFoundError { message: "payment method auth connector account not found".to_string(), })?; @@ -2494,6 +2490,10 @@ pub async fn retrieve_payment_connector( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let mca = store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( key_manager_state, @@ -2506,7 +2506,15 @@ pub async fn retrieve_payment_connector( id: merchant_connector_id.clone(), })?; - Ok(service_api::ApplicationResponse::Json(mca.try_into()?)) + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let mca: domain::MerchantConnectorAccount = { + let _ = &merchant_connector_id; + todo!() + }; + + Ok(service_api::ApplicationResponse::Json( + mca.foreign_try_into()?, + )) } pub async fn list_payment_connectors( @@ -2543,7 +2551,7 @@ pub async fn list_payment_connectors( // The can be eliminated once [#79711](https://github.com/rust-lang/rust/issues/79711) is stabilized for mca in merchant_connector_accounts.into_iter() { - response.push(mca.try_into()?); + response.push(mca.foreign_try_into()?); } Ok(service_api::ApplicationResponse::Json(response)) @@ -2571,6 +2579,10 @@ pub async fn update_payment_connector( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( key_manager_state, @@ -2583,11 +2595,19 @@ pub async fn update_payment_connector( id: merchant_connector_id.to_string(), })?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let mca: domain::MerchantConnectorAccount = { + let _ = &merchant_connector_id; + let _ = &req; + let _ = &merchant_account; + todo!() + }; let payment_methods_enabled = req.payment_methods_enabled.map(|pm_enabled| { pm_enabled .iter() .flat_map(Encode::encode_to_value) - .collect::>() + .map(Secret::new) + .collect::>>() }); let frm_configs = get_frm_config_as_secret(req.frm_configs); @@ -2637,6 +2657,10 @@ pub async fn update_payment_connector( .await?; } } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let payment_connector = storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(req.connector_type), connector_name: None, @@ -2676,6 +2700,43 @@ pub async fn update_payment_connector( ) .await?, }; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let payment_connector = storage::MerchantConnectorAccountUpdate::Update { + connector_type: Some(req.connector_type), + connector_label: req.connector_label.clone(), + connector_account_details: req + .connector_account_details + .async_lift(|inner| { + domain_types::encrypt_optional( + key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.get_inner().peek(), + ) + }) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting data")?, + disabled, + payment_methods_enabled, + metadata: req.metadata, + frm_configs, + connector_webhook_details: match &req.connector_webhook_details { + Some(connector_webhook_details) => connector_webhook_details + .encode_to_value() + .change_context(errors::ApiErrorResponse::InternalServerError) + .map(Some)? + .map(Secret::new), + None => None, + }, + applepay_verified_domains: None, + pm_auth_config: req.pm_auth_config, + status: Some(connector_status), + connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( + &state, &key_store, &metadata, + ) + .await?, + }; // Profile id should always be present let profile_id = mca @@ -2704,7 +2765,7 @@ pub async fn update_payment_connector( format!("Failed while updating MerchantConnectorAccount: id: {merchant_connector_id}") })?; - let response = updated_mca.try_into()?; + let response = updated_mca.foreign_try_into()?; Ok(service_api::ApplicationResponse::Json(response)) } @@ -2730,6 +2791,10 @@ pub async fn delete_payment_connector( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let _mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( key_manager_state, @@ -2742,6 +2807,16 @@ pub async fn delete_payment_connector( id: merchant_connector_id.clone(), })?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + let _ = merchant_connector_id; + todo!() + }; + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let is_deleted = db .delete_merchant_connector_account_by_merchant_id_merchant_connector_id( &merchant_id, @@ -2752,6 +2827,9 @@ pub async fn delete_payment_connector( id: merchant_connector_id.clone(), })?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let is_deleted = { todo!() }; + let response = api::MerchantConnectorDeleteResponse { merchant_id, merchant_connector_id, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index dfd8e085f674..df3a5a2ca2f9 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -156,6 +156,10 @@ where .await .attach_printable("Could not find profile id from business details")?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let merchant_connector_account_from_db_option = db .find_merchant_connector_account_by_profile_id_connector_name( &state.into(), @@ -169,6 +173,16 @@ where }) .ok(); + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let merchant_connector_account_from_db_option: Option< + domain::MerchantConnectorAccount, + > = { + let _ = key_store; + let _ = frm_routing_algorithm_struct; + let _ = profile_id; + todo!() + }; + match merchant_connector_account_from_db_option { Some(merchant_connector_account_from_db) => { let frm_configs_option = merchant_connector_account_from_db diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index a0b5f7755f2d..8cb64d1e0d83 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2385,7 +2385,7 @@ pub async fn list_payment_methods( }; filter_payment_methods( &graph, - mca.merchant_connector_id.clone(), + mca.get_id(), payment_methods, &mut req, &mut response, @@ -2401,10 +2401,7 @@ pub async fn list_payment_methods( // No PM_FILTER_CGRAPH Cache present in MokaCache let mut builder = cgraph::ConstraintGraphBuilder::new(); for mca in &filtered_mcas { - let domain_id = builder.make_domain( - mca.merchant_connector_id.clone(), - mca.connector_name.as_str(), - ); + let domain_id = builder.make_domain(mca.get_id(), mca.connector_name.as_str()); let Ok(domain_id) = domain_id else { logger::error!("Failed to construct domain for list payment methods"); @@ -2440,7 +2437,7 @@ pub async fn list_payment_methods( }; filter_payment_methods( &graph, - mca.merchant_connector_id.clone(), + mca.get_id().clone(), payment_methods, &mut req, &mut response, @@ -2647,8 +2644,7 @@ pub async fn list_payment_methods( .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; let matched_mca = filtered_mcas.iter().find(|m| { - first_routable_connector.merchant_connector_id.as_ref() - == Some(&m.merchant_connector_id) + first_routable_connector.merchant_connector_id.as_ref() == Some(&m.get_id()) }); if let Some(m) = matched_mca { @@ -3374,7 +3370,7 @@ pub async fn call_surcharge_decision_management_for_saved_card( pub async fn filter_payment_methods( graph: &cgraph::ConstraintGraph, mca_id: String, - payment_methods: &[serde_json::Value], + payment_methods: &[Secret], req: &mut api::PaymentMethodListRequest, resp: &mut Vec, payment_intent: Option<&storage::PaymentIntent>, @@ -3384,7 +3380,9 @@ pub async fn filter_payment_methods( saved_payment_methods: &settings::EligiblePaymentMethods, ) -> errors::CustomResult<(), errors::ApiErrorResponse> { for payment_method in payment_methods.iter() { - let parse_result = serde_json::from_value::(payment_method.clone()); + let parse_result = serde_json::from_value::( + payment_method.clone().expose().clone(), + ); if let Ok(payment_methods_enabled) = parse_result { let payment_method = payment_methods_enabled.payment_method; @@ -4043,7 +4041,7 @@ pub async fn get_mca_status( .collect::>(); for mca in mcas { - mca_ids.insert(mca.merchant_connector_id); + mca_ids.insert(mca.get_id()); } for mca_id in connector_mandate_details.keys() { diff --git a/crates/router/src/core/payment_methods/utils.rs b/crates/router/src/core/payment_methods/utils.rs index 7d90a5cb6bc0..0aad71908931 100644 --- a/crates/router/src/core/payment_methods/utils.rs +++ b/crates/router/src/core/payment_methods/utils.rs @@ -9,6 +9,7 @@ use common_enums::enums; use euclid::frontend::dir; use hyperswitch_constraint_graph as cgraph; use kgraph_utils::{error::KgraphError, transformers::IntoDirValue}; +use masking::ExposeInterface; use storage_impl::redis::cache::{CacheKey, PM_FILTERS_CGRAPH_CACHE}; use crate::{configs::settings, routes::SessionState}; @@ -16,14 +17,15 @@ use crate::{configs::settings, routes::SessionState}; pub fn make_pm_graph( builder: &mut cgraph::ConstraintGraphBuilder, domain_id: cgraph::DomainId, - payment_methods: &[serde_json::value::Value], + payment_methods: &[masking::Secret], connector: String, pm_config_mapping: &settings::ConnectorFilters, supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, ) -> Result<(), KgraphError> { for payment_method in payment_methods.iter() { - let pm_enabled = serde_json::from_value::(payment_method.clone()); + let pm_enabled = + serde_json::from_value::(payment_method.clone().expose()); if let Ok(payment_methods_enabled) = pm_enabled { compile_pm_graph( builder, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 6c598e67a348..c00d6be21cb9 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3281,6 +3281,10 @@ impl MerchantConnectorAccountType { } } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] pub fn is_test_mode_on(&self) -> Option { match self { Self::DbVal(val) => val.test_mode, @@ -3288,9 +3292,14 @@ impl MerchantConnectorAccountType { } } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + pub fn is_test_mode_on(&self) -> Option { + None + } + pub fn get_mca_id(&self) -> Option { match self { - Self::DbVal(db_val) => Some(db_val.merchant_connector_id.to_string()), + Self::DbVal(db_val) => Some(db_val.get_id()), Self::CacheVal(_) => None, } } @@ -3325,7 +3334,7 @@ pub async fn get_merchant_connector_account( merchant_connector_id: Option<&String>, ) -> RouterResult { let db = &*state.store; - let key_manager_state = &state.into(); + let key_manager_state: &KeyManagerState = &state.into(); match creds_identifier { Some(creds_identifier) => { let key = merchant_id.get_creds_identifier_key(&creds_identifier); @@ -3399,35 +3408,64 @@ pub async fn get_merchant_connector_account( Ok(MerchantConnectorAccountType::CacheVal(res)) } None => { - if let Some(merchant_connector_id) = merchant_connector_id { - db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( - key_manager_state, - merchant_id, - merchant_connector_id, - key_store, - ) - .await - .to_not_found_response( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id.to_string(), - }, - ) - } else { - db.find_merchant_connector_account_by_profile_id_connector_name( - key_manager_state, - profile_id, - connector_name, - key_store, - ) - .await - .to_not_found_response( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: format!("profile id {profile_id} and connector name {connector_name}"), - }, - ) - } + let mca: RouterResult = + if let Some(merchant_connector_id) = merchant_connector_id { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, + merchant_id, + merchant_connector_id, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: merchant_connector_id.to_string(), + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + // get mca using id + { + let _id = merchant_connector_id; + let _ = key_store; + let _ = profile_id; + let _ = connector_name; + let _ = key_manager_state; + todo!() + } + } else { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, + profile_id, + connector_name, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: format!( + "profile id {profile_id} and connector name {connector_name}" + ), + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + todo!() + } + }; + mca.map(MerchantConnectorAccountType::DbVal) } - .map(MerchantConnectorAccountType::DbVal), } } @@ -4261,7 +4299,7 @@ where for merchant_connector_account in profile_specific_merchant_connector_account_list { if is_apple_pay_simplified_flow( - merchant_connector_account.metadata, + merchant_connector_account.metadata.clone(), merchant_connector_account .connector_wallets_details .as_deref() @@ -4272,7 +4310,7 @@ where &state.conf.connectors, &merchant_connector_account.connector_name.to_string(), api::GetToken::Connector, - Some(merchant_connector_account.merchant_connector_id), + Some(merchant_connector_account.get_id()), ) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Invalid connector name received")?; @@ -5135,7 +5173,7 @@ pub async fn validate_merchant_connector_ids_in_connector_mandate_details( .iter() .map(|merchant_connector_account| { ( - merchant_connector_account.merchant_connector_id.clone(), + merchant_connector_account.get_id(), merchant_connector_account.clone(), ) }) diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index e65fcbc390b8..5e384021e54a 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -445,17 +445,30 @@ where &state.conf.connectors, &merchant_connector_account.connector_name.to_string(), connector_type, - Some(merchant_connector_account.merchant_connector_id.clone()), + Some(merchant_connector_account.get_id()), ) .map_err(|err| { logger::error!(session_token_error=?err); err }) { - session_connector_data.push(api::SessionConnectorData { - payment_method_type, - connector: connector_data, - business_sub_label: merchant_connector_account.business_sub_label.clone(), - }) + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + let new_session_connector_data = api::SessionConnectorData::new( + payment_method_type, + connector_data, + merchant_connector_account.business_sub_label.clone(), + ); + session_connector_data.push(new_session_connector_data) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + let new_session_connector_data = + api::SessionConnectorData::new(payment_method_type, connector_data, None); + session_connector_data.push(new_session_connector_data) + } }; } diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 151192fb8b79..f823f6846f8b 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -45,7 +45,7 @@ use crate::{ types::{ api::{self, routing as routing_types}, domain, storage as oss_storage, - transformers::{ForeignFrom, ForeignInto}, + transformers::{ForeignFrom, ForeignInto, ForeignTryFrom}, }, utils::{OptionExt, ValueExt}, SessionState, @@ -567,7 +567,7 @@ pub async fn refresh_cgraph_cache<'a>( let api_mcas = merchant_connector_accounts .into_iter() - .map(admin_api::MerchantConnectorResponse::try_from) + .map(admin_api::MerchantConnectorResponse::foreign_try_from) .collect::, _>>() .change_context(errors::RoutingError::KgraphCacheRefreshFailed)?; let connector_configs = state diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index d4dd01b4ddc1..77b5c1463f06 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -264,6 +264,8 @@ pub async fn filter_payout_methods( key_store: &domain::MerchantKeyStore, payout: &hyperswitch_domain_models::payouts::payouts::Payouts, ) -> errors::RouterResult> { + use masking::ExposeInterface; + let db = &*state.store; let key_manager_state = &state.into(); //Fetch all merchant connector accounts @@ -311,7 +313,7 @@ pub async fn filter_payout_methods( }; for payout_method in payout_methods.iter() { let parse_result = serde_json::from_value::( - payout_method.clone(), + payout_method.clone().expose(), ); if let Ok(payment_methods_enabled) = parse_result { let payment_method = payment_methods_enabled.payment_method; diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index f2d5d2d7d5e9..8d237da5ed56 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -136,6 +136,10 @@ pub async fn create_link_token( .and_then(|address| address.country) .map(|country| country.to_string()); + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( @@ -149,6 +153,12 @@ pub async fn create_link_token( id: merchant_account.get_id().get_string_repr().to_owned(), })?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let merchant_connector_account = { + let _ = billing_country; + todo!() + }; + let auth_type = helpers::get_connector_auth_type(merchant_connector_account)?; let router_data = pm_auth_types::LinkTokenRouterData { @@ -231,6 +241,10 @@ pub async fn exchange_token_core( let connector = PaymentAuthConnectorData::get_connector_by_name(connector_name)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( @@ -244,6 +258,14 @@ pub async fn exchange_token_core( id: merchant_account.get_id().get_string_repr().to_owned(), })?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let merchant_connector_account: domain::MerchantConnectorAccount = { + let _ = merchant_account; + let _ = connector; + let _ = key_store; + todo!() + }; + let auth_type = helpers::get_connector_auth_type(merchant_connector_account.clone())?; let access_token = get_access_token_from_exchange_api( @@ -273,7 +295,7 @@ pub async fn exchange_token_core( state, bank_account_details_resp, (connector_name, access_token), - merchant_connector_account.merchant_connector_id, + merchant_connector_account.get_id(), )) .await?; @@ -716,6 +738,10 @@ pub async fn retrieve_payment_method_from_auth_service( .await .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( key_manager_state, @@ -731,6 +757,13 @@ pub async fn retrieve_payment_method_from_auth_service( "error while fetching merchant_connector_account from merchant_id and connector name", )?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let mca = { + let _ = merchant_account; + let _ = connector; + todo!() + }; + let auth_type = pm_auth_helpers::get_connector_auth_type(mca)?; let BankAccountAccessCreds::AccessToken(access_token) = diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index f2c230f0b864..a6591c640daa 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -993,9 +993,7 @@ pub async fn get_filters_for_refunds( .connector_label .clone() .map(|label| { - let info = merchant_connector_account - .clone() - .to_merchant_connector_info(&label.clone()); + let info = merchant_connector_account.to_merchant_connector_info(&label); (merchant_connector_account.connector_name, info) }) }) diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index d4729d1e629d..6ef668839507 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -412,7 +412,7 @@ pub async fn validate_connectors_in_routing_config( let name_mca_id_set = all_mcas .iter() .filter(|mca| mca.profile_id.as_deref() == Some(profile_id)) - .map(|mca| (&mca.connector_name, &mca.merchant_connector_id)) + .map(|mca| (&mca.connector_name, mca.get_id())) .collect::>(); let name_set = all_mcas @@ -424,7 +424,7 @@ pub async fn validate_connectors_in_routing_config( let connector_choice = |choice: &routing_types::RoutableConnectorChoice| { if let Some(ref mca_id) = choice.merchant_connector_id { error_stack::ensure!( - name_mca_id_set.contains(&(&choice.connector.to_string(), mca_id)), + name_mca_id_set.contains(&(&choice.connector.to_string(), mca_id.clone())), errors::ApiErrorResponse::InvalidRequestData { message: format!( "connector with name '{}' and merchant connector account id '{}' not found for the given profile", diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 6eb1dd572dde..ee11a3813653 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -653,7 +653,7 @@ pub async fn backfill_metadata( user.to_owned(), DBEnum::StripeConnected, types::MetaData::StripeConnected(api::ProcessorConnected { - processor_id: mca.merchant_connector_id, + processor_id: mca.get_id(), processor_name: mca.connector_name, }), ) @@ -693,7 +693,7 @@ pub async fn backfill_metadata( user.to_owned(), DBEnum::PaypalConnected, types::MetaData::PaypalConnected(api::ProcessorConnected { - processor_id: mca.merchant_connector_id, + processor_id: mca.get_id(), processor_name: mca.connector_name, }), ) @@ -711,18 +711,32 @@ pub async fn get_merchant_connector_account_by_name( connector_name: &str, key_store: &MerchantKeyStore, ) -> UserResult> { - state - .store - .find_merchant_connector_account_by_merchant_id_connector_name( - &state.into(), - merchant_id, - connector_name, - key_store, - ) - .await - .map_err(|e| { - e.change_context(UserErrors::InternalServerError) - .attach_printable("DB Error Fetching DashboardMetaData") - }) - .map(|data| data.first().cloned()) + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + state + .store + .find_merchant_connector_account_by_merchant_id_connector_name( + &state.into(), + merchant_id, + connector_name, + key_store, + ) + .await + .map_err(|e| { + e.change_context(UserErrors::InternalServerError) + .attach_printable("DB Error Fetching DashboardMetaData") + }) + .map(|data| data.first().cloned()) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + let _ = state; + let _ = merchant_id; + let _ = connector_name; + let _ = key_store; + todo!() + } } diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index 724f244d3977..c5bb43f12c85 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -101,6 +101,10 @@ pub async fn get_verified_apple_domains_with_mid_mca_id( .await .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let verified_domains = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( key_manager_state, @@ -113,6 +117,13 @@ pub async fn get_verified_apple_domains_with_mid_mca_id( .applepay_verified_domains .unwrap_or_default(); + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let verified_domains = { + let _ = merchant_connector_id; + let _ = key_store; + todo!() + }; + Ok(services::api::ApplicationResponse::Json( verifications::ApplepayVerifiedDomainsResponse { verified_domains }, )) diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index c286497e5cf6..43a4bfdc73b9 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -26,6 +26,10 @@ pub async fn check_existence_and_add_domain_to_db( .await .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( @@ -37,6 +41,14 @@ pub async fn check_existence_and_add_domain_to_db( .await .change_context(errors::ApiErrorResponse::InternalServerError)?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let merchant_connector_account: hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount = { + let _ = merchant_connector_id; + let _ = key_store; + let _ = domain_from_req; + todo!() + }; + let mut already_verified_domains = merchant_connector_account .applepay_verified_domains .clone() @@ -48,6 +60,10 @@ pub async fn check_existence_and_add_domain_to_db( .collect(); already_verified_domains.append(&mut new_verified_domains); + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let updated_mca = storage::MerchantConnectorAccountUpdate::Update { connector_type: None, connector_name: None, @@ -65,6 +81,21 @@ pub async fn check_existence_and_add_domain_to_db( status: None, connector_wallets_details: None, }; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let updated_mca = storage::MerchantConnectorAccountUpdate::Update { + connector_type: None, + connector_account_details: None, + disabled: None, + payment_methods_enabled: None, + metadata: None, + frm_configs: None, + connector_webhook_details: None, + applepay_verified_domains: Some(already_verified_domains.clone()), + pm_auth_config: None, + connector_label: None, + status: None, + connector_wallets_details: None, + }; state .store .update_merchant_connector_account( diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 61207aebb9ee..86ac8d49d69b 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -1673,6 +1673,10 @@ async fn fetch_optional_mca_and_connector( > { let db = &state.store; if connector_name_or_mca_id.starts_with("mca_") { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( &state.into(), @@ -1687,11 +1691,16 @@ async fn fetch_optional_mca_and_connector( .attach_printable( "error while fetching merchant_connector_account from connector_id", )?; - let (connector, connector_name) = get_connector_by_connector_name( - state, - &mca.connector_name, - Some(mca.merchant_connector_id.clone()), - )?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let mca: domain::MerchantConnectorAccount = { + let _ = merchant_account; + let _ = key_store; + let _ = db; + todo!() + }; + + let (connector, connector_name) = + get_connector_by_connector_name(state, &mca.connector_name, Some(mca.get_id()))?; Ok((Some(mca), connector, connector_name)) } else { diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 02b434445110..63282bddc3ea 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1018,6 +1018,10 @@ impl MerchantConnectorAccountInterface for KafkaStore { .update_multiple_merchant_connector_accounts(merchant_connector_accounts) .await } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, state: &KeyManagerState, @@ -1035,6 +1039,10 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, state: &KeyManagerState, @@ -1052,6 +1060,10 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_profile_id_connector_name( &self, state: &KeyManagerState, @@ -1080,6 +1092,10 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, state: &KeyManagerState, @@ -1097,6 +1113,18 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn find_by_merchant_connector_account_id( + &self, + state: &KeyManagerState, + id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + self.diesel_store + .find_by_merchant_connector_account_id(state, id, key_store) + .await + } + async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, state: &KeyManagerState, @@ -1126,6 +1154,10 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id( &self, merchant_id: &id_type::MerchantId, @@ -1138,6 +1170,16 @@ impl MerchantConnectorAccountInterface for KafkaStore { ) .await } + + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn delete_merchant_connector_account_by_id( + &self, + id: &str, + ) -> CustomResult { + self.diesel_store + .delete_merchant_connector_account_by_id(id) + .await + } } #[async_trait::async_trait] diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index 31f5e285c49b..80019012c0b2 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -122,6 +122,10 @@ where NewDstType = storage::MerchantConnectorAccountNew, >, { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, state: &KeyManagerState, @@ -130,6 +134,10 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_profile_id_connector_name( &self, state: &KeyManagerState, @@ -138,6 +146,10 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, state: &KeyManagerState, @@ -153,6 +165,10 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, state: &KeyManagerState, @@ -161,6 +177,14 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn find_by_merchant_connector_account_id( + &self, + state: &KeyManagerState, + id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult; + async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, state: &KeyManagerState, @@ -185,15 +209,29 @@ where )>, ) -> CustomResult<(), errors::StorageError>; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id( &self, merchant_id: &common_utils::id_type::MerchantId, merchant_connector_id: &str, ) -> CustomResult; + + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn delete_merchant_connector_account_by_id( + &self, + id: &str, + ) -> CustomResult; } #[async_trait::async_trait] impl MerchantConnectorAccountInterface for Store { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, @@ -244,6 +282,10 @@ impl MerchantConnectorAccountInterface for Store { } } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] #[instrument(skip_all)] async fn find_merchant_connector_account_by_profile_id_connector_name( &self, @@ -298,6 +340,10 @@ impl MerchantConnectorAccountInterface for Store { } } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, @@ -333,6 +379,10 @@ impl MerchantConnectorAccountInterface for Store { } #[instrument(skip_all)] + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, state: &KeyManagerState, @@ -387,6 +437,50 @@ impl MerchantConnectorAccountInterface for Store { } } + #[instrument(skip_all)] + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn find_by_merchant_connector_account_id( + &self, + state: &KeyManagerState, + id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let find_call = || async { + let conn = connection::pg_connection_read(self).await?; + storage::MerchantConnectorAccount::find_by_id(&conn, id) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + }; + + #[cfg(not(feature = "accounts_cache"))] + { + find_call() + .await? + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[cfg(feature = "accounts_cache")] + { + cache::get_or_populate_in_memory(self, id, find_call, &cache::ACCOUNTS_CACHE) + .await? + .convert( + state, + key_store.key.get_inner(), + common_utils::types::keymanager::Identifier::Merchant( + key_store.merchant_id.clone(), + ), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + } + #[instrument(skip_all)] async fn insert_merchant_connector_account( &self, @@ -479,8 +573,7 @@ impl MerchantConnectorAccountInterface for Store { )?; let _merchant_id = merchant_connector_account.merchant_id.clone(); - let _merchant_connector_id = - merchant_connector_account.merchant_connector_id.clone(); + let _merchant_connector_id = merchant_connector_account.get_id().clone(); let update = update_call( &connection_pool, @@ -548,6 +641,10 @@ impl MerchantConnectorAccountInterface for Store { } #[instrument(skip_all)] + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn update_merchant_connector_account( &self, state: &KeyManagerState, @@ -626,6 +723,91 @@ impl MerchantConnectorAccountInterface for Store { } #[instrument(skip_all)] + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn update_merchant_connector_account( + &self, + state: &KeyManagerState, + this: domain::MerchantConnectorAccount, + merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let _connector_name = this.connector_name.clone(); + let _profile_id = this + .profile_id + .clone() + .ok_or(errors::StorageError::ValueNotFound( + "profile_id".to_string(), + ))?; + + let _merchant_id = this.merchant_id.clone(); + let _merchant_connector_id = this.get_id().clone(); + + let update_call = || async { + let conn = connection::pg_connection_write(self).await?; + Conversion::convert(this) + .await + .change_context(errors::StorageError::EncryptionError)? + .update(&conn, merchant_connector_account) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + .async_and_then(|item| async { + item.convert( + state, + key_store.key.get_inner(), + common_utils::types::keymanager::Identifier::Merchant( + key_store.merchant_id.clone(), + ), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + }; + + #[cfg(feature = "accounts_cache")] + { + // Redact all caches as any of might be used because of backwards compatibility + cache::publish_and_redact_multiple( + self, + [ + cache::CacheKind::Accounts( + format!("{}_{}", _profile_id, _connector_name).into(), + ), + cache::CacheKind::Accounts( + format!( + "{}_{}", + _merchant_id.get_string_repr(), + _merchant_connector_id + ) + .into(), + ), + cache::CacheKind::CGraph( + format!("cgraph_{}_{_profile_id}", _merchant_id.get_string_repr()).into(), + ), + cache::CacheKind::PmFiltersCGraph( + format!( + "pm_filters_cgraph_{}_{_profile_id}", + _merchant_id.get_string_repr() + ) + .into(), + ), + ], + update_call, + ) + .await + } + + #[cfg(not(feature = "accounts_cache"))] + { + update_call().await + } + } + + #[instrument(skip_all)] + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id( &self, merchant_id: &common_utils::id_type::MerchantId, @@ -689,6 +871,63 @@ impl MerchantConnectorAccountInterface for Store { delete_call().await } } + + #[instrument(skip_all)] + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn delete_merchant_connector_account_by_id( + &self, + id: &str, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + let delete_call = || async { + storage::MerchantConnectorAccount::delete_by_id(&conn, id) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + }; + + #[cfg(feature = "accounts_cache")] + { + // We need to fetch mca here because the key that's saved in cache in + // {merchant_id}_{connector_label}. + // Used function from storage model to reuse the connection that made here instead of + // creating new. + + let mca = storage::MerchantConnectorAccount::find_by_id(&conn, id) + .await + .map_err(|error| report!(errors::StorageError::from(error)))?; + + let _profile_id = mca.profile_id.ok_or(errors::StorageError::ValueNotFound( + "profile_id".to_string(), + ))?; + + cache::publish_and_redact_multiple( + self, + [ + cache::CacheKind::Accounts( + format!("{}_{}", mca.merchant_id.get_string_repr(), _profile_id).into(), + ), + cache::CacheKind::CGraph( + format!("cgraph_{}_{_profile_id}", mca.merchant_id.get_string_repr()) + .into(), + ), + cache::CacheKind::PmFiltersCGraph( + format!( + "pm_filters_cgraph_{}_{_profile_id}", + mca.merchant_id.get_string_repr() + ) + .into(), + ), + ], + delete_call, + ) + .await + } + + #[cfg(not(feature = "accounts_cache"))] + { + delete_call().await + } + } } #[async_trait::async_trait] @@ -704,6 +943,10 @@ impl MerchantConnectorAccountInterface for MockDb { // apple pay certificate migration Err(errors::StorageError::MockDbError)? } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, state: &KeyManagerState, @@ -743,6 +986,10 @@ impl MerchantConnectorAccountInterface for MockDb { } } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, state: &KeyManagerState, @@ -776,6 +1023,10 @@ impl MerchantConnectorAccountInterface for MockDb { Ok(output) } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_merchant_connector_account_by_profile_id_connector_name( &self, state: &KeyManagerState, @@ -811,6 +1062,10 @@ impl MerchantConnectorAccountInterface for MockDb { } } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, state: &KeyManagerState, @@ -850,6 +1105,48 @@ impl MerchantConnectorAccountInterface for MockDb { } } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn find_by_merchant_connector_account_id( + &self, + state: &KeyManagerState, + id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + match self + .merchant_connector_accounts + .lock() + .await + .iter() + .find(|account| account.get_id() == id) + .cloned() + .async_map(|account| async { + account + .convert( + state, + key_store.key.get_inner(), + common_utils::types::keymanager::Identifier::Merchant( + key_store.merchant_id.clone(), + ), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + { + Some(result) => result, + None => { + return Err(errors::StorageError::ValueNotFound( + "cannot find merchant connector account".to_string(), + ) + .into()) + } + } + } + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn insert_merchant_connector_account( &self, state: &KeyManagerState, @@ -894,6 +1191,48 @@ impl MerchantConnectorAccountInterface for MockDb { .change_context(errors::StorageError::DecryptionError) } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn insert_merchant_connector_account( + &self, + state: &KeyManagerState, + t: domain::MerchantConnectorAccount, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let mut accounts = self.merchant_connector_accounts.lock().await; + let account = storage::MerchantConnectorAccount { + id: t.id, + merchant_id: t.merchant_id, + connector_name: t.connector_name, + connector_account_details: t.connector_account_details.into(), + disabled: t.disabled, + payment_methods_enabled: t.payment_methods_enabled, + metadata: t.metadata, + frm_config: t.frm_configs, + connector_type: t.connector_type, + connector_label: t.connector_label, + created_at: common_utils::date_time::now(), + modified_at: common_utils::date_time::now(), + connector_webhook_details: t.connector_webhook_details, + profile_id: t.profile_id, + applepay_verified_domains: t.applepay_verified_domains, + pm_auth_config: t.pm_auth_config, + status: t.status, + connector_wallets_details: t.connector_wallets_details.map(Encryption::from), + additional_merchant_data: t.additional_merchant_data.map(|data| data.into()), + }; + accounts.push(account.clone()); + account + .convert( + state, + key_store.key.get_inner(), + common_utils::types::keymanager::Identifier::Merchant( + key_store.merchant_id.clone(), + ), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, state: &KeyManagerState, @@ -932,6 +1271,10 @@ impl MerchantConnectorAccountInterface for MockDb { Ok(output) } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn update_merchant_connector_account( &self, state: &KeyManagerState, @@ -974,6 +1317,55 @@ impl MerchantConnectorAccountInterface for MockDb { } } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn update_merchant_connector_account( + &self, + state: &KeyManagerState, + this: domain::MerchantConnectorAccount, + merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let mca_update_res = self + .merchant_connector_accounts + .lock() + .await + .iter_mut() + .find(|account| account.get_id() == this.get_id()) + .map(|a| { + let updated = + merchant_connector_account.create_merchant_connector_account(a.clone()); + *a = updated.clone(); + updated + }) + .async_map(|account| async { + account + .convert( + state, + key_store.key.get_inner(), + common_utils::types::keymanager::Identifier::Merchant( + key_store.merchant_id.clone(), + ), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await; + + match mca_update_res { + Some(result) => result, + None => { + return Err(errors::StorageError::ValueNotFound( + "cannot find merchant connector account to update".to_string(), + ) + .into()) + } + } + } + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn delete_merchant_connector_account_by_merchant_id_merchant_connector_id( &self, merchant_id: &common_utils::id_type::MerchantId, @@ -996,6 +1388,26 @@ impl MerchantConnectorAccountInterface for MockDb { } } } + + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn delete_merchant_connector_account_by_id( + &self, + id: &str, + ) -> CustomResult { + let mut accounts = self.merchant_connector_accounts.lock().await; + match accounts.iter().position(|account| account.get_id() == id) { + Some(index) => { + accounts.remove(index); + return Ok(true); + } + None => { + return Err(errors::StorageError::ValueNotFound( + "cannot find merchant connector account to delete".to_string(), + ) + .into()) + } + } + } } #[cfg(feature = "accounts_cache")] @@ -1003,6 +1415,10 @@ impl MerchantConnectorAccountInterface for MockDb { mod merchant_connector_account_cache_tests { use std::sync::Arc; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] use api_models::enums::CountryAlpha2; use common_utils::{date_time, types::keymanager::Identifier}; use diesel_models::enums::ConnectorType; @@ -1035,6 +1451,10 @@ mod merchant_connector_account_cache_tests { #[allow(clippy::unwrap_used)] #[tokio::test] + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] async fn test_connector_profile_id_cache() { let conf = Settings::new().unwrap(); let tx: oneshot::Sender<()> = oneshot::channel().0; @@ -1192,4 +1612,161 @@ mod merchant_connector_account_cache_tests { .await .is_none()) } + + #[allow(clippy::unwrap_used)] + #[tokio::test] + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + async fn test_connector_profile_id_cache() { + let conf = Settings::new().unwrap(); + let tx: oneshot::Sender<()> = oneshot::channel().0; + + let app_state = Box::pin(routes::AppState::with_storage( + conf, + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); + #[allow(clippy::expect_used)] + let db = MockDb::new(&redis_interface::RedisSettings::default()) + .await + .expect("Failed to create Mock store"); + + let redis_conn = db.get_redis_conn().unwrap(); + let master_key = db.get_master_key(); + redis_conn + .subscribe("hyperswitch_invalidate") + .await + .unwrap(); + + let merchant_id = + common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from("test_merchant")) + .unwrap(); + let connector_label = "stripe_USA"; + let id = "simple_id"; + let profile_id = "pro_max_ultra"; + let key_manager_state = &state.into(); + db.insert_merchant_key_store( + key_manager_state, + domain::MerchantKeyStore { + merchant_id: merchant_id.clone(), + key: domain::types::encrypt( + key_manager_state, + services::generate_aes256_key().unwrap().to_vec().into(), + Identifier::Merchant(merchant_id.clone()), + master_key, + ) + .await + .unwrap(), + created_at: datetime!(2023-02-01 0:00), + }, + &master_key.to_vec().into(), + ) + .await + .unwrap(); + + let merchant_key = db + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &master_key.to_vec().into(), + ) + .await + .unwrap(); + + let mca = domain::MerchantConnectorAccount { + id: id.to_string(), + merchant_id: merchant_id.clone(), + connector_name: "stripe".to_string(), + connector_account_details: domain::types::encrypt( + key_manager_state, + serde_json::Value::default().into(), + Identifier::Merchant(merchant_key.merchant_id.clone()), + merchant_key.key.get_inner().peek(), + ) + .await + .unwrap(), + disabled: None, + payment_methods_enabled: None, + connector_type: ConnectorType::FinOperations, + metadata: None, + frm_configs: None, + connector_label: Some(connector_label.to_string()), + created_at: date_time::now(), + modified_at: date_time::now(), + connector_webhook_details: None, + profile_id: Some(profile_id.to_string()), + applepay_verified_domains: None, + pm_auth_config: None, + status: common_enums::ConnectorStatus::Inactive, + connector_wallets_details: Some( + domain::types::encrypt( + key_manager_state, + serde_json::Value::default().into(), + Identifier::Merchant(merchant_key.merchant_id.clone()), + merchant_key.key.get_inner().peek(), + ) + .await + .unwrap(), + ), + additional_merchant_data: None, + }; + + db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key) + .await + .unwrap(); + + let find_call = || async { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + let mca = db + .find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, + profile_id, + &mca.connector_name, + &merchant_key, + ) + .await + .unwrap(); + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let mca: domain::MerchantConnectorAccount = { todo!() }; + Conversion::convert(mca) + .await + .change_context(errors::StorageError::DecryptionError) + }; + + let _: storage::MerchantConnectorAccount = cache::get_or_populate_in_memory( + &db, + &format!("{}_{}", merchant_id.clone().get_string_repr(), profile_id), + find_call, + &ACCOUNTS_CACHE, + ) + .await + .unwrap(); + + let delete_call = || async { db.delete_merchant_connector_account_by_id(id).await }; + + cache::publish_and_redact( + &db, + CacheKind::Accounts( + format!("{}_{}", merchant_id.get_string_repr(), connector_label).into(), + ), + delete_call, + ) + .await + .unwrap(); + + assert!(ACCOUNTS_CACHE + .get_val::(CacheKey { + key: format!("{}_{}", merchant_id.get_string_repr(), connector_label), + prefix: String::default(), + },) + .await + .is_none()) + } } diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index c85b60d13d4f..550b1c7af140 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -211,6 +211,20 @@ pub struct SessionConnectorData { pub business_sub_label: Option, } +impl SessionConnectorData { + pub fn new( + payment_method_type: api_enums::PaymentMethodType, + connector: ConnectorData, + business_sub_label: Option, + ) -> Self { + Self { + payment_method_type, + connector, + business_sub_label, + } + } +} + /// Session Surcharge type pub enum SessionSurchargeDetails { /// Surcharge is calculated by hyperswitch diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs index 86d367c1f198..8c7cb1ed71ba 100644 --- a/crates/router/src/types/domain/merchant_connector_account.rs +++ b/crates/router/src/types/domain/merchant_connector_account.rs @@ -1,256 +1 @@ -use common_utils::{ - crypto::Encryptable, - date_time, - encryption::Encryption, - errors::{CustomResult, ValidationError}, - pii, - types::keymanager::{Identifier, KeyManagerState}, -}; -use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; -use error_stack::ResultExt; -use masking::{PeekInterface, Secret}; - -use super::{ - behaviour, - types::{decrypt, decrypt_optional, AsyncLift}, -}; -#[derive(Clone, Debug)] -pub struct MerchantConnectorAccount { - pub merchant_id: common_utils::id_type::MerchantId, - pub connector_name: String, - pub connector_account_details: Encryptable>, - pub test_mode: Option, - pub disabled: Option, - pub merchant_connector_id: String, - pub payment_methods_enabled: Option>, - pub connector_type: enums::ConnectorType, - pub metadata: Option, - pub frm_configs: Option>>, - pub connector_label: Option, - pub business_country: Option, - pub business_label: Option, - pub business_sub_label: Option, - pub created_at: time::PrimitiveDateTime, - pub modified_at: time::PrimitiveDateTime, - pub connector_webhook_details: Option, - pub profile_id: Option, - pub applepay_verified_domains: Option>, - pub pm_auth_config: Option, - pub status: enums::ConnectorStatus, - pub connector_wallets_details: Option>>, - pub additional_merchant_data: Option>>, -} - -#[derive(Debug)] -pub enum MerchantConnectorAccountUpdate { - Update { - connector_type: Option, - connector_name: Option, - connector_account_details: Option>>, - test_mode: Option, - disabled: Option, - merchant_connector_id: Option, - payment_methods_enabled: Option>, - metadata: Option, - frm_configs: Option>>, - connector_webhook_details: Option, - applepay_verified_domains: Option>, - pm_auth_config: Option, - connector_label: Option, - status: Option, - connector_wallets_details: Option>>, - }, - ConnectorWalletDetailsUpdate { - connector_wallets_details: Encryptable>, - }, -} - -#[async_trait::async_trait] -impl behaviour::Conversion for MerchantConnectorAccount { - type DstType = diesel_models::merchant_connector_account::MerchantConnectorAccount; - type NewDstType = diesel_models::merchant_connector_account::MerchantConnectorAccountNew; - - async fn convert(self) -> CustomResult { - Ok( - diesel_models::merchant_connector_account::MerchantConnectorAccount { - merchant_id: self.merchant_id, - connector_name: self.connector_name, - connector_account_details: self.connector_account_details.into(), - test_mode: self.test_mode, - disabled: self.disabled, - merchant_connector_id: self.merchant_connector_id, - payment_methods_enabled: self.payment_methods_enabled, - connector_type: self.connector_type, - metadata: self.metadata, - frm_configs: None, - frm_config: self.frm_configs, - business_country: self.business_country, - business_label: self.business_label, - connector_label: self.connector_label, - business_sub_label: self.business_sub_label, - created_at: self.created_at, - modified_at: self.modified_at, - connector_webhook_details: self.connector_webhook_details, - profile_id: self.profile_id, - applepay_verified_domains: self.applepay_verified_domains, - pm_auth_config: self.pm_auth_config, - status: self.status, - connector_wallets_details: self.connector_wallets_details.map(Encryption::from), - additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), - }, - ) - } - - async fn convert_back( - state: &KeyManagerState, - other: Self::DstType, - key: &Secret>, - _key_manager_identifier: Identifier, - ) -> CustomResult { - let identifier = Identifier::Merchant(other.merchant_id.clone()); - Ok(Self { - merchant_id: other.merchant_id, - connector_name: other.connector_name, - connector_account_details: decrypt( - state, - other.connector_account_details, - identifier.clone(), - key.peek(), - ) - .await - .change_context(ValidationError::InvalidValue { - message: "Failed while decrypting connector account details".to_string(), - })?, - test_mode: other.test_mode, - disabled: other.disabled, - merchant_connector_id: other.merchant_connector_id, - payment_methods_enabled: other.payment_methods_enabled, - connector_type: other.connector_type, - metadata: other.metadata, - - frm_configs: other.frm_config, - business_country: other.business_country, - business_label: other.business_label, - connector_label: other.connector_label, - business_sub_label: other.business_sub_label, - created_at: other.created_at, - modified_at: other.modified_at, - connector_webhook_details: other.connector_webhook_details, - profile_id: other.profile_id, - applepay_verified_domains: other.applepay_verified_domains, - pm_auth_config: other.pm_auth_config, - status: other.status, - connector_wallets_details: other - .connector_wallets_details - .async_lift(|inner| decrypt_optional(state, inner, identifier.clone(), key.peek())) - .await - .change_context(ValidationError::InvalidValue { - message: "Failed while decrypting connector wallets details".to_string(), - })?, - additional_merchant_data: if let Some(data) = other.additional_merchant_data { - Some( - decrypt(state, data, identifier, key.peek()) - .await - .change_context(ValidationError::InvalidValue { - message: "Failed while decrypting additional_merchant_data".to_string(), - })?, - ) - } else { - None - }, - }) - } - - async fn construct_new(self) -> CustomResult { - let now = date_time::now(); - Ok(Self::NewDstType { - merchant_id: Some(self.merchant_id), - connector_name: Some(self.connector_name), - connector_account_details: Some(self.connector_account_details.into()), - test_mode: self.test_mode, - disabled: self.disabled, - merchant_connector_id: self.merchant_connector_id, - payment_methods_enabled: self.payment_methods_enabled, - connector_type: Some(self.connector_type), - metadata: self.metadata, - frm_configs: None, - frm_config: self.frm_configs, - business_country: self.business_country, - business_label: self.business_label, - connector_label: self.connector_label, - business_sub_label: self.business_sub_label, - created_at: now, - modified_at: now, - connector_webhook_details: self.connector_webhook_details, - profile_id: self.profile_id, - applepay_verified_domains: self.applepay_verified_domains, - pm_auth_config: self.pm_auth_config, - status: self.status, - connector_wallets_details: self.connector_wallets_details.map(Encryption::from), - additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), - }) - } -} - -impl From for MerchantConnectorAccountUpdateInternal { - fn from(merchant_connector_account_update: MerchantConnectorAccountUpdate) -> Self { - match merchant_connector_account_update { - MerchantConnectorAccountUpdate::Update { - connector_type, - connector_name, - connector_account_details, - test_mode, - disabled, - merchant_connector_id, - payment_methods_enabled, - metadata, - frm_configs, - connector_webhook_details, - applepay_verified_domains, - pm_auth_config, - connector_label, - status, - connector_wallets_details, - } => Self { - connector_type, - connector_name, - connector_account_details: connector_account_details.map(Encryption::from), - test_mode, - disabled, - merchant_connector_id, - payment_methods_enabled, - metadata, - frm_configs: None, - frm_config: frm_configs, - modified_at: Some(date_time::now()), - connector_webhook_details, - applepay_verified_domains, - pm_auth_config, - connector_label, - status, - connector_wallets_details: connector_wallets_details.map(Encryption::from), - }, - MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate { - connector_wallets_details, - } => Self { - connector_wallets_details: Some(Encryption::from(connector_wallets_details)), - connector_type: None, - connector_name: None, - connector_account_details: None, - connector_label: None, - test_mode: None, - disabled: None, - merchant_connector_id: None, - payment_methods_enabled: None, - frm_configs: None, - metadata: None, - modified_at: None, - connector_webhook_details: None, - frm_config: None, - applepay_verified_domains: None, - pm_auth_config: None, - status: None, - }, - } - } -} +pub use hyperswitch_domain_models::merchant_connector_account::*; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index ce59a85dc9ba..d25d9e9c39d1 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -923,15 +923,21 @@ impl ForeignFrom for api_models::cards_info } } -impl TryFrom +impl ForeignTryFrom for api_models::admin::MerchantConnectorListResponse { type Error = error_stack::Report; - fn try_from(item: domain::MerchantConnectorAccount) -> Result { + fn foreign_try_from(item: domain::MerchantConnectorAccount) -> Result { let payment_methods_enabled = match item.payment_methods_enabled { - Some(val) => serde_json::Value::Array(val) - .parse_value("PaymentMethods") - .change_context(errors::ApiErrorResponse::InternalServerError)?, + Some(secret_val) => { + let val = secret_val + .into_iter() + .map(|secret| secret.expose()) + .collect(); + serde_json::Value::Array(val) + .parse_value("PaymentMethods") + .change_context(errors::ApiErrorResponse::InternalServerError)? + } None => None, }; let frm_configs = match item.frm_configs { @@ -952,6 +958,10 @@ impl TryFrom } None => None, }; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let response = Self { connector_type: item.connector_type, connector_name: item.connector_name, @@ -983,21 +993,57 @@ impl TryFrom .transpose()? .map(api_models::admin::AdditionalMerchantData::foreign_from), }; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + let response = Self { + id: item.id, + connector_type: item.connector_type, + connector_name: item.connector_name, + connector_label: item.connector_label, + disabled: item.disabled, + payment_methods_enabled, + metadata: item.metadata, + frm_configs, + profile_id: item.profile_id, + applepay_verified_domains: item.applepay_verified_domains, + pm_auth_config: item.pm_auth_config, + status: item.status, + additional_merchant_data: item + .additional_merchant_data + .map(|data| { + let data = data.into_inner(); + serde_json::Value::parse_value::( + data.expose(), + "AdditionalMerchantData", + ) + .attach_printable("Unable to deserialize additional_merchant_data") + .change_context(errors::ApiErrorResponse::InternalServerError) + }) + .transpose()? + .map(api_models::admin::AdditionalMerchantData::foreign_from), + }; Ok(response) } } -impl TryFrom for api_models::admin::MerchantConnectorResponse { +impl ForeignTryFrom + for api_models::admin::MerchantConnectorResponse +{ type Error = error_stack::Report; - fn try_from(item: domain::MerchantConnectorAccount) -> Result { - let payment_methods_enabled = match item.payment_methods_enabled { - Some(val) => serde_json::Value::Array(val) - .parse_value("PaymentMethods") - .change_context(errors::ApiErrorResponse::InternalServerError)?, + fn foreign_try_from(item: domain::MerchantConnectorAccount) -> Result { + let payment_methods_enabled = match item.payment_methods_enabled.clone() { + Some(secret_val) => { + let val = secret_val + .into_iter() + .map(|secret| secret.expose()) + .collect(); + serde_json::Value::Array(val) + .parse_value("PaymentMethods") + .change_context(errors::ApiErrorResponse::InternalServerError)? + } None => None, }; let frm_configs = match item.frm_configs { - Some(frm_value) => { + Some(ref frm_value) => { let configs_for_frm : Vec = frm_value .iter() .map(|config| { config @@ -1016,10 +1062,10 @@ impl TryFrom for api_models::admin::MerchantCo }; #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] let response = Self { + id: item.get_id(), connector_type: item.connector_type, connector_name: item.connector_name, connector_label: item.connector_label, - connector_id: item.merchant_connector_id, connector_account_details: item.connector_account_details.into_inner(), disabled: item.disabled, payment_methods_enabled, diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 6798cdc3ea39..1e8761d1375e 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -366,16 +366,31 @@ pub async fn find_mca_from_authentication_id_type( .to_not_found_response(errors::ApiErrorResponse::InternalServerError)? } }; - db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( - &state.into(), - merchant_account.get_id(), - &authentication.merchant_connector_id, - key_store, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: authentication.merchant_connector_id.to_string(), - }) + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &state.into(), + merchant_account.get_id(), + &authentication.merchant_connector_id, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: authentication.merchant_connector_id.to_string(), + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + //get mca using id + { + let _ = key_store; + let _ = authentication; + todo!() + } } pub async fn get_mca_from_payment_intent( @@ -394,19 +409,37 @@ pub async fn get_mca_from_payment_intent( ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - let key_manager_state = &state.into(); + let key_manager_state: &KeyManagerState = &state.into(); match payment_attempt.merchant_connector_id { - Some(merchant_connector_id) => db - .find_by_merchant_connector_account_merchant_id_merchant_connector_id( - key_manager_state, - merchant_account.get_id(), - &merchant_connector_id, - key_store, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id, - }), + Some(merchant_connector_id) => { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, + merchant_account.get_id(), + &merchant_connector_id, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: merchant_connector_id, + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + //get mca using id + let _id = merchant_connector_id; + let _ = key_store; + let _ = key_manager_state; + let _ = connector_name; + todo!() + } + } None => { let profile_id = match payment_intent.profile_id { Some(profile_id) => profile_id, @@ -422,19 +455,30 @@ pub async fn get_mca_from_payment_intent( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("profile_id is not set in payment_intent")?, }; - - db.find_merchant_connector_account_by_profile_id_connector_name( - key_manager_state, - &profile_id, - connector_name, - key_store, - ) - .await - .to_not_found_response( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: format!("profile_id {profile_id} and connector_name {connector_name}"), - }, - ) + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, + &profile_id, + connector_name, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: format!("profile_id {profile_id} and connector_name {connector_name}"), + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + //get mca using id + let _ = profile_id; + todo!() + } } } } @@ -466,33 +510,64 @@ pub async fn get_mca_from_payout_attempt( .await .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?, }; - let key_manager_state = &state.into(); + let key_manager_state: &KeyManagerState = &state.into(); match payout.merchant_connector_id { - Some(merchant_connector_id) => db - .find_by_merchant_connector_account_merchant_id_merchant_connector_id( - key_manager_state, - merchant_account.get_id(), - &merchant_connector_id, - key_store, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_connector_id, - }), - None => db - .find_merchant_connector_account_by_profile_id_connector_name( - key_manager_state, - &payout.profile_id, - connector_name, - key_store, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: format!( - "profile_id {} and connector_name {}", - payout.profile_id, connector_name - ), - }), + Some(merchant_connector_id) => { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, + merchant_account.get_id(), + &merchant_connector_id, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: merchant_connector_id, + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + //get mca using id + let _id = merchant_connector_id; + let _ = key_store; + let _ = connector_name; + let _ = key_manager_state; + todo!() + } + } + None => { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, + &payout.profile_id, + connector_name, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: format!( + "profile_id {} and connector_name {}", + payout.profile_id, connector_name + ), + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + todo!() + } + } } } @@ -505,17 +580,32 @@ pub async fn get_mca_from_object_reference_id( ) -> CustomResult { let db = &*state.store; match merchant_account.default_profile.as_ref() { - Some(profile_id) => db - .find_merchant_connector_account_by_profile_id_connector_name( - &state.into(), - profile_id, - connector_name, - key_store, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: format!("profile_id {profile_id} and connector_name {connector_name}"), - }), + Some(profile_id) => { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] + { + db.find_merchant_connector_account_by_profile_id_connector_name( + &state.into(), + profile_id, + connector_name, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: format!("profile_id {profile_id} and connector_name {connector_name}"), + }, + ) + } + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + let _ = db; + let _ = profile_id; + todo!() + } + } _ => match object_reference_id { webhooks::ObjectReferenceId::PaymentId(payment_id_type) => { get_mca_from_payment_intent( diff --git a/crates/router/src/utils/connector_onboarding.rs b/crates/router/src/utils/connector_onboarding.rs index b66190f450f1..7813a7cb31f9 100644 --- a/crates/router/src/utils/connector_onboarding.rs +++ b/crates/router/src/utils/connector_onboarding.rs @@ -56,6 +56,10 @@ pub async fn check_if_connector_exists( .await .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_connector_account_v2") + ))] let _connector = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( @@ -69,6 +73,13 @@ pub async fn check_if_connector_exists( id: connector_id.to_string(), })?; + #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] + { + let _ = connector_id; + let _ = key_store; + todo!() + }; + Ok(()) } diff --git a/v2_migrations/2024-07-31-065721_mca_v2/down.sql b/v2_migrations/2024-07-31-065721_mca_v2/down.sql new file mode 100644 index 000000000000..167cfd8fe5c9 --- /dev/null +++ b/v2_migrations/2024-07-31-065721_mca_v2/down.sql @@ -0,0 +1,21 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS frm_configs jsonb; + +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS merchant_connector_id VARCHAR(32); + +ALTER TABLE merchant_connector_account DROP CONSTRAINT merchant_connector_account_pkey; + +DROP INDEX IF EXISTS merchant_connector_account_id_index; + +UPDATE merchant_connector_account SET merchant_connector_id = id; + +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS id; + +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS id SERIAL; + +ALTER TABLE merchant_connector_account ADD PRIMARY KEY (merchant_connector_id); + +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS business_country "CountryAlpha2"; +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS business_label VARCHAR(255); +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS business_sub_label VARCHAR(64); +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS test_mode BOOLEAN; diff --git a/v2_migrations/2024-07-31-065721_mca_v2/up.sql b/v2_migrations/2024-07-31-065721_mca_v2/up.sql new file mode 100644 index 000000000000..ca7859af4e13 --- /dev/null +++ b/v2_migrations/2024-07-31-065721_mca_v2/up.sql @@ -0,0 +1,26 @@ +-- Your SQL goes here +-- This migration is to remove the business_country, business_label, business_sub_label, and test_mode columns from the merchant_connector_account table +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS business_country; +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS business_label; +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS business_sub_label; +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS test_mode; + +-- This migration is to modify the id column in merchant_connector_account table to be a VARCHAR(64) and to set the id column as primary key +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS id; + +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS id VARCHAR(64); + +-- Backfill the id column with the merchant_connector_id to prevent null values +UPDATE merchant_connector_account SET id = merchant_connector_id; + +CREATE UNIQUE INDEX merchant_connector_account_id_index ON merchant_connector_account (id); + +ALTER TABLE merchant_connector_account DROP CONSTRAINT merchant_connector_account_pkey; + +ALTER TABLE merchant_connector_account ADD PRIMARY KEY (id); + +-- This migration is to remove the merchant_connector_id column from the merchant_connector_account table +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS merchant_connector_id; + +-- This migration is to remove the frm_configs column from the merchant_connector_account table +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS frm_configs; \ No newline at end of file From fb32b61edfa2b4190a5717850aeca6b3b0d7db54 Mon Sep 17 00:00:00 2001 From: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Date: Thu, 1 Aug 2024 18:54:49 +0530 Subject: [PATCH 5/7] feat(core): accept business profile in core functions for payments, refund, payout and disputes (#5498) --- .../compatibility/stripe/payment_intents.rs | 9 ++++++- .../src/compatibility/stripe/refunds.rs | 4 +++- .../src/compatibility/stripe/setup_intents.rs | 4 ++++ crates/router/src/core/disputes.rs | 6 +++++ .../fraud_check/operation/fraud_check_post.rs | 2 ++ crates/router/src/core/payments.rs | 8 +++++++ crates/router/src/core/payouts.rs | 3 +++ crates/router/src/core/refunds.rs | 16 +++++++++++-- crates/router/src/core/webhooks/incoming.rs | 6 +++++ crates/router/src/routes/disputes.rs | 14 ++++++----- crates/router/src/routes/payments.rs | 24 ++++++++++++++++--- crates/router/src/routes/payouts.rs | 8 ++++--- crates/router/src/routes/refunds.rs | 10 +++++--- .../src/workflows/outgoing_webhook_retry.rs | 4 +++- crates/router/tests/payments.rs | 2 ++ crates/router/tests/payments2.rs | 2 ++ 16 files changed, 102 insertions(+), 20 deletions(-) diff --git a/crates/router/src/compatibility/stripe/payment_intents.rs b/crates/router/src/compatibility/stripe/payment_intents.rs index c9881647ed93..4acace4b182d 100644 --- a/crates/router/src/compatibility/stripe/payment_intents.rs +++ b/crates/router/src/compatibility/stripe/payment_intents.rs @@ -63,6 +63,7 @@ pub async fn payment_intents_create( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentCreate, req, @@ -123,6 +124,7 @@ pub async fn payment_intents_retrieve( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentStatus, payload, @@ -193,6 +195,7 @@ pub async fn payment_intents_retrieve_with_gateway_creds( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentStatus, req, @@ -259,6 +262,7 @@ pub async fn payment_intents_update( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentUpdate, req, @@ -331,6 +335,7 @@ pub async fn payment_intents_confirm( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentConfirm, req, @@ -392,6 +397,7 @@ pub async fn payment_intents_capture( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentCapture, payload, @@ -457,6 +463,7 @@ pub async fn payment_intents_cancel( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentCancel, req, @@ -499,7 +506,7 @@ pub async fn payment_intent_list( &req, payload, |state, auth, req, _| { - payments::list_payments(state, auth.merchant_account, auth.key_store, req) + payments::list_payments(state, auth.merchant_account, None, auth.key_store, req) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/compatibility/stripe/refunds.rs b/crates/router/src/compatibility/stripe/refunds.rs index 071fdd839eb3..565cec5855d3 100644 --- a/crates/router/src/compatibility/stripe/refunds.rs +++ b/crates/router/src/compatibility/stripe/refunds.rs @@ -50,7 +50,7 @@ pub async fn refund_create( &req, create_refund_req, |state, auth, req, _| { - refunds::refund_create_core(state, auth.merchant_account, auth.key_store, req) + refunds::refund_create_core(state, auth.merchant_account, None, auth.key_store, req) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, @@ -97,6 +97,7 @@ pub async fn refund_retrieve_with_gateway_creds( refunds::refund_response_wrapper( state, auth.merchant_account, + None, auth.key_store, refund_request, refunds::refund_retrieve_core, @@ -139,6 +140,7 @@ pub async fn refund_retrieve( refunds::refund_response_wrapper( state, auth.merchant_account, + None, auth.key_store, refund_request, refunds::refund_retrieve_core, diff --git a/crates/router/src/compatibility/stripe/setup_intents.rs b/crates/router/src/compatibility/stripe/setup_intents.rs index 28933e74bfb5..cfe0958d7d93 100644 --- a/crates/router/src/compatibility/stripe/setup_intents.rs +++ b/crates/router/src/compatibility/stripe/setup_intents.rs @@ -64,6 +64,7 @@ pub async fn setup_intents_create( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentCreate, req, @@ -124,6 +125,7 @@ pub async fn setup_intents_retrieve( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentStatus, payload, @@ -196,6 +198,7 @@ pub async fn setup_intents_update( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentUpdate, req, @@ -269,6 +272,7 @@ pub async fn setup_intents_confirm( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentConfirm, req, diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 0584851e6073..37f6d111de42 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -26,6 +26,7 @@ use crate::{ pub async fn retrieve_dispute( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, req: disputes::DisputeId, ) -> RouterResponse { let dispute = state @@ -43,6 +44,7 @@ pub async fn retrieve_dispute( pub async fn retrieve_disputes_list( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id_list: Option>, constraints: api_models::disputes::DisputeListConstraints, ) -> RouterResponse> { let disputes = state @@ -62,6 +64,7 @@ pub async fn retrieve_disputes_list( pub async fn accept_dispute( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, req: disputes::DisputeId, ) -> RouterResponse { @@ -164,6 +167,7 @@ pub async fn accept_dispute( pub async fn submit_evidence( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, req: dispute_models::SubmitEvidenceRequest, ) -> RouterResponse { @@ -329,6 +333,7 @@ pub async fn submit_evidence( pub async fn attach_evidence( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, attach_evidence_request: api::AttachEvidenceRequest, ) -> RouterResponse { @@ -406,6 +411,7 @@ pub async fn attach_evidence( pub async fn retrieve_dispute_evidence( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, req: disputes::DisputeId, ) -> RouterResponse> { let dispute = state diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index ca7f110e8993..41e4d25d378f 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -215,6 +215,7 @@ impl Domain for FraudCheckPost { state.clone(), req_state.clone(), merchant_account.clone(), + None, key_store.clone(), payments::PaymentCancel, cancel_req, @@ -270,6 +271,7 @@ impl Domain for FraudCheckPost { state.clone(), req_state.clone(), merchant_account.clone(), + None, key_store.clone(), payments::PaymentCapture, capture_request, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index f1dce992d171..e2593ddd2311 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -788,6 +788,7 @@ pub async fn payments_core( state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, operation: Op, req: Req, @@ -998,6 +999,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { state.clone(), req_state, merchant_account, + None, merchant_key_store, payment_complete_authorize::CompleteAuthorize, payment_confirm_req, @@ -1129,6 +1131,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { state.clone(), req_state, merchant_account, + None, merchant_key_store, PaymentStatus, payment_sync_req, @@ -1286,6 +1289,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { state.clone(), req_state, merchant_account, + None, merchant_key_store, PaymentConfirm, payment_confirm_req, @@ -1316,6 +1320,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { state.clone(), req_state, merchant_account.clone(), + None, merchant_key_store, PaymentStatus, payment_sync_req, @@ -2866,6 +2871,7 @@ pub fn is_operation_complete_authorize(operation: &Op) -> bool { pub async fn list_payments( state: SessionState, merchant: domain::MerchantAccount, + _profile_id_list: Option>, key_store: domain::MerchantKeyStore, constraints: api::PaymentListConstraints, ) -> RouterResponse { @@ -2938,6 +2944,7 @@ pub async fn list_payments( pub async fn apply_filters_on_payments( state: SessionState, merchant: domain::MerchantAccount, + _profile_id_list: Option>, merchant_key_store: domain::MerchantKeyStore, constraints: api::PaymentListFilterConstraints, ) -> RouterResponse { @@ -3035,6 +3042,7 @@ pub async fn get_filters_for_payments( pub async fn get_payment_filters( state: SessionState, merchant: domain::MerchantAccount, + _profile_id_list: Option>, ) -> RouterResponse { let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) = super::admin::list_payment_connectors(state, merchant.get_id().to_owned()).await? diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 3f6cfd117417..75756a963909 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -482,6 +482,7 @@ pub async fn payouts_update_core( pub async fn payouts_retrieve_core( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, req: payouts::PayoutRetrieveRequest, ) -> RouterResponse { @@ -707,6 +708,7 @@ pub async fn payouts_fulfill_core( pub async fn payouts_list_core( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id_list: Option>, key_store: domain::MerchantKeyStore, constraints: payouts::PayoutListConstraints, ) -> RouterResponse { @@ -805,6 +807,7 @@ pub async fn payouts_list_core( pub async fn payouts_filtered_list_core( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id_list: Option>, key_store: domain::MerchantKeyStore, filters: payouts::PayoutListFilterConstraints, ) -> RouterResponse { diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index a6591c640daa..5868b0621749 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -47,6 +47,7 @@ use crate::{ pub async fn refund_create_core( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, req: refunds::RefundRequest, ) -> RouterResponse { @@ -373,17 +374,24 @@ where pub async fn refund_response_wrapper<'a, F, Fut, T, Req>( state: SessionState, merchant_account: domain::MerchantAccount, + profile_id: Option, key_store: domain::MerchantKeyStore, request: Req, f: F, ) -> RouterResponse where - F: Fn(SessionState, domain::MerchantAccount, domain::MerchantKeyStore, Req) -> Fut, + F: Fn( + SessionState, + domain::MerchantAccount, + Option, + domain::MerchantKeyStore, + Req, + ) -> Fut, Fut: futures::Future>, T: ForeignInto, { Ok(services::ApplicationResponse::Json( - f(state, merchant_account, key_store, request) + f(state, merchant_account, profile_id, key_store, request) .await? .foreign_into(), )) @@ -393,6 +401,7 @@ where pub async fn refund_retrieve_core( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id: Option, key_store: domain::MerchantKeyStore, request: refunds::RefundsRetrieveRequest, ) -> RouterResult { @@ -856,6 +865,7 @@ pub async fn validate_and_create_refund( pub async fn refund_list( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id_list: Option>, req: api_models::refunds::RefundListRequest, ) -> RouterResponse { let db = state.store; @@ -977,6 +987,7 @@ pub async fn refund_manual_update( pub async fn get_filters_for_refunds( state: SessionState, merchant_account: domain::MerchantAccount, + _profile_id_list: Option>, ) -> RouterResponse { let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) = super::admin::list_payment_connectors(state, merchant_account.get_id().to_owned()).await? @@ -1157,6 +1168,7 @@ pub async fn sync_refund_with_gateway_workflow( let response = Box::pin(refund_retrieve_core( state.clone(), merchant_account, + None, key_store, refunds::RefundsRetrieveRequest { refund_id: refund_core.refund_internal_reference_id, diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 86ac8d49d69b..22d295b11669 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -544,6 +544,7 @@ async fn payments_incoming_webhook_flow( state.clone(), req_state, merchant_account.clone(), + None, key_store.clone(), payments::operations::PaymentStatus, api::PaymentsRetrieveRequest { @@ -824,6 +825,7 @@ async fn refunds_incoming_webhook_flow( Box::pin(refunds::refund_retrieve_core( state.clone(), merchant_account.clone(), + None, key_store.clone(), api_models::refunds::RefundsRetrieveRequest { refund_id: refund_id.to_owned(), @@ -1075,6 +1077,7 @@ async fn external_authentication_incoming_webhook_flow( state.clone(), req_state, merchant_account.clone(), + None, key_store.clone(), payments::PaymentConfirm, payment_confirm_req, @@ -1268,6 +1271,7 @@ async fn frm_incoming_webhook_flow( state.clone(), req_state, merchant_account.clone(), + None, key_store.clone(), payments::PaymentApprove, api::PaymentsCaptureRequest { @@ -1293,6 +1297,7 @@ async fn frm_incoming_webhook_flow( state.clone(), req_state, merchant_account.clone(), + None, key_store.clone(), payments::PaymentReject, api::PaymentsCancelRequest { @@ -1459,6 +1464,7 @@ async fn bank_transfer_webhook_flow( state.clone(), req_state, merchant_account.to_owned(), + None, key_store.clone(), payments::PaymentConfirm, request, diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index 1c2377952a1b..3eed0591dbae 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -43,7 +43,7 @@ pub async fn retrieve_dispute( state, &req, dispute_id, - |state, auth, req, _| disputes::retrieve_dispute(state, auth.merchant_account, req), + |state, auth, req, _| disputes::retrieve_dispute(state, auth.merchant_account, None, req), auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::DisputeRead), @@ -90,7 +90,9 @@ pub async fn retrieve_disputes_list( state, &req, payload, - |state, auth, req, _| disputes::retrieve_disputes_list(state, auth.merchant_account, req), + |state, auth, req, _| { + disputes::retrieve_disputes_list(state, auth.merchant_account, None, req) + }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::DisputeRead), @@ -131,7 +133,7 @@ pub async fn accept_dispute( &req, dispute_id, |state, auth, req, _| { - disputes::accept_dispute(state, auth.merchant_account, auth.key_store, req) + disputes::accept_dispute(state, auth.merchant_account, None, auth.key_store, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -168,7 +170,7 @@ pub async fn submit_dispute_evidence( &req, json_payload.into_inner(), |state, auth, req, _| { - disputes::submit_evidence(state, auth.merchant_account, auth.key_store, req) + disputes::submit_evidence(state, auth.merchant_account, None, auth.key_store, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -213,7 +215,7 @@ pub async fn attach_dispute_evidence( &req, attach_evidence_request, |state, auth, req, _| { - disputes::attach_evidence(state, auth.merchant_account, auth.key_store, req) + disputes::attach_evidence(state, auth.merchant_account, None, auth.key_store, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -256,7 +258,7 @@ pub async fn retrieve_dispute_evidence( &req, dispute_id, |state, auth, req, _| { - disputes::retrieve_dispute_evidence(state, auth.merchant_account, req) + disputes::retrieve_dispute_evidence(state, auth.merchant_account, None, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 9f6c35cacd8b..58d331d0fe3c 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -200,6 +200,7 @@ pub async fn payments_start( state, req_state, auth.merchant_account, + None, auth.key_store, payments::operations::PaymentStart, req, @@ -275,6 +276,7 @@ pub async fn payments_retrieve( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentStatus, req, @@ -348,6 +350,7 @@ pub async fn payments_retrieve_with_gateway_creds( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentStatus, req, @@ -555,6 +558,7 @@ pub async fn payments_capture( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentCapture, payload, @@ -624,6 +628,7 @@ pub async fn payments_connector_session( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentSession, payload, @@ -856,6 +861,7 @@ pub async fn payments_complete_authorize( state.clone(), req_state, auth.merchant_account, + None, auth.key_store, payments::operations::payment_complete_authorize::CompleteAuthorize, payment_confirm_req.clone(), @@ -915,6 +921,7 @@ pub async fn payments_cancel( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentCancel, req, @@ -969,7 +976,7 @@ pub async fn payments_list( &req, payload, |state, auth, req, _| { - payments::list_payments(state, auth.merchant_account, auth.key_store, req) + payments::list_payments(state, auth.merchant_account, None, auth.key_store, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -995,7 +1002,13 @@ pub async fn payments_list_by_filter( &req, payload, |state, auth: auth::AuthenticationData, req, _| { - payments::apply_filters_on_payments(state, auth.merchant_account, auth.key_store, req) + payments::apply_filters_on_payments( + state, + auth.merchant_account, + None, + auth.key_store, + req, + ) }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, @@ -1038,7 +1051,7 @@ pub async fn get_payment_filters( &req, (), |state, auth: auth::AuthenticationData, _, _| { - payments::get_payment_filters(state, auth.merchant_account) + payments::get_payment_filters(state, auth.merchant_account, None) }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, @@ -1075,6 +1088,7 @@ pub async fn payments_approve( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentApprove, payment_types::PaymentsCaptureRequest { @@ -1129,6 +1143,7 @@ pub async fn payments_reject( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentReject, payment_types::PaymentsCancelRequest { @@ -1196,6 +1211,7 @@ where state, req_state, merchant_account, + None, key_store, operation, req, @@ -1216,6 +1232,7 @@ where state, req_state, merchant_account, + None, key_store, operation, req, @@ -1278,6 +1295,7 @@ pub async fn payments_incremental_authorization( state, req_state, auth.merchant_account, + None, auth.key_store, payments::PaymentIncrementalAuthorization, req, diff --git a/crates/router/src/routes/payouts.rs b/crates/router/src/routes/payouts.rs index d32aa0da3ebe..18ba815551eb 100644 --- a/crates/router/src/routes/payouts.rs +++ b/crates/router/src/routes/payouts.rs @@ -80,7 +80,7 @@ pub async fn payouts_retrieve( &req, payout_retrieve_request, |state, auth, req, _| { - payouts_retrieve_core(state, auth.merchant_account, auth.key_store, req) + payouts_retrieve_core(state, auth.merchant_account, None, auth.key_store, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -272,7 +272,9 @@ pub async fn payouts_list( state, &req, payload, - |state, auth, req, _| payouts_list_core(state, auth.merchant_account, auth.key_store, req), + |state, auth, req, _| { + payouts_list_core(state, auth.merchant_account, None, auth.key_store, req) + }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::PayoutRead), @@ -311,7 +313,7 @@ pub async fn payouts_list_by_filter( &req, payload, |state, auth, req, _| { - payouts_filtered_list_core(state, auth.merchant_account, auth.key_store, req) + payouts_filtered_list_core(state, auth.merchant_account, None, auth.key_store, req) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index 013ed064975f..e0b1e0786fff 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -36,7 +36,9 @@ pub async fn refunds_create( state, &req, json_payload.into_inner(), - |state, auth, req, _| refund_create_core(state, auth.merchant_account, auth.key_store, req), + |state, auth, req, _| { + refund_create_core(state, auth.merchant_account, None, auth.key_store, req) + }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::RefundWrite), @@ -92,6 +94,7 @@ pub async fn refunds_retrieve( refund_response_wrapper( state, auth.merchant_account, + None, auth.key_store, refund_request, refund_retrieve_core, @@ -143,6 +146,7 @@ pub async fn refunds_retrieve_with_body( refund_response_wrapper( state, auth.merchant_account, + None, auth.key_store, req, refund_retrieve_core, @@ -220,7 +224,7 @@ pub async fn refunds_list( state, &req, payload.into_inner(), - |state, auth, req, _| refund_list(state, auth.merchant_account, req), + |state, auth, req, _| refund_list(state, auth.merchant_account, None, req), auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::RefundRead), @@ -291,7 +295,7 @@ pub async fn get_refunds_filters(state: web::Data, req: HttpRequest) - state, &req, (), - |state, auth, _, _| get_filters_for_refunds(state, auth.merchant_account), + |state, auth, _, _| get_filters_for_refunds(state, auth.merchant_account, None), auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::RefundRead), diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index 5334e4e93a9c..df9f00a2e045 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -371,6 +371,7 @@ async fn get_outgoing_webhook_content_and_event_type( state, req_state, merchant_account, + None, key_store, PaymentStatus, request, @@ -417,6 +418,7 @@ async fn get_outgoing_webhook_content_and_event_type( let refund = Box::pin(refund_retrieve_core( state, merchant_account, + None, key_store, request, )) @@ -436,7 +438,7 @@ async fn get_outgoing_webhook_content_and_event_type( let request = DisputeId { dispute_id }; let dispute_response = - match retrieve_dispute(state, merchant_account, request).await? { + match retrieve_dispute(state, merchant_account, None, request).await? { ApplicationResponse::Json(dispute_response) | ApplicationResponse::JsonWithHeaders((dispute_response, _)) => { Ok(dispute_response) diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 8a5235a536c2..0f5dbf286a3f 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -385,6 +385,7 @@ async fn payments_create_core() { state.clone(), state.get_req_state(), merchant_account, + None, key_store, payments::PaymentCreate, req, @@ -571,6 +572,7 @@ async fn payments_create_core_adyen_no_redirect() { state.clone(), state.get_req_state(), merchant_account, + None, key_store, payments::PaymentCreate, req, diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index aa984b9a0ee8..f5497b4293fa 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -145,6 +145,7 @@ async fn payments_create_core() { state.clone(), state.get_req_state(), merchant_account, + None, key_store, payments::PaymentCreate, req, @@ -339,6 +340,7 @@ async fn payments_create_core_adyen_no_redirect() { state.clone(), state.get_req_state(), merchant_account, + None, key_store, payments::PaymentCreate, req, From b4eb6016a4e696acf155732592a6571363c24e64 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Thu, 1 Aug 2024 19:10:36 +0530 Subject: [PATCH 6/7] feat(auth): Add `profile_id` in `AuthenticationData` (#5492) --- crates/router/src/core/user.rs | 17 ++++- crates/router/src/core/user_role.rs | 8 +- crates/router/src/db/merchant_account.rs | 1 + crates/router/src/services/authentication.rs | 74 +++++++++++++++++++ crates/router/src/types/domain/user.rs | 8 +- .../src/types/domain/user/decision_manager.rs | 11 ++- crates/router/src/utils/user.rs | 5 +- 7 files changed, 112 insertions(+), 12 deletions(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 40741c761986..457a71dff8bd 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -138,7 +138,9 @@ pub async fn signup( .await?; utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; - let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; + let token = + utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role) + .await?; let response = utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?; @@ -894,6 +896,7 @@ async fn handle_new_user_invitation( merchant_id: user_from_token.merchant_id.clone(), org_id: user_from_token.org_id.clone(), role_id: request.role_id.clone(), + profile_id: None, }; let set_metadata_request = SetMetaDataRequest::IsChangePasswordRequired; @@ -1036,8 +1039,12 @@ pub async fn accept_invite_from_email( .change_context(UserErrors::InternalServerError)? .into(); - let token = - utils::user::generate_jwt_auth_token(&state, &user_from_db, &update_status_result).await?; + let token = utils::user::generate_jwt_auth_token_without_profile( + &state, + &user_from_db, + &update_status_result, + ) + .await?; utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &update_status_result) .await; @@ -1263,6 +1270,7 @@ pub async fn switch_merchant_id( request.merchant_id.clone(), org_id.clone(), user_from_token.role_id.clone(), + None, ) .await?; @@ -1295,7 +1303,8 @@ pub async fn switch_merchant_id( .ok_or(report!(UserErrors::InvalidRoleOperation)) .attach_printable("User doesn't have access to switch")?; - let token = utils::user::generate_jwt_auth_token(&state, &user, user_role).await?; + let token = + utils::user::generate_jwt_auth_token_without_profile(&state, &user, user_role).await?; utils::user_role::set_role_permissions_in_cache_by_user_role(&state, user_role).await; (token, user_role.role_id.clone()) diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index a728836857c7..b5c7f6db0168 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -169,7 +169,9 @@ pub async fn transfer_org_ownership( utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; - let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; + let token = + utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role) + .await?; let response = utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token.clone())?; @@ -246,7 +248,9 @@ pub async fn merchant_select( utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; - let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; + let token = + utils::user::generate_jwt_auth_token_without_profile(&state, &user_from_db, &user_role) + .await?; let response = utils::user::get_dashboard_entry_response( &state, user_from_db, diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index d707ec17c3a0..2e43d4698153 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -273,6 +273,7 @@ impl MerchantAccountInterface for Store { .change_context(errors::StorageError::DecryptionError)?, key_store, + profile_id: None, }) } diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index df59083537f1..0b2066fdb13f 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -54,6 +54,14 @@ mod detached; pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, pub key_store: domain::MerchantKeyStore, + pub profile_id: Option, +} + +#[derive(Clone)] +pub struct AuthenticationDataWithMultipleProfiles { + pub merchant_account: domain::MerchantAccount, + pub key_store: domain::MerchantKeyStore, + pub profile_id_list: Option>, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] @@ -178,6 +186,7 @@ pub struct AuthToken { pub role_id: String, pub exp: u64, pub org_id: id_type::OrganizationId, + pub profile_id: Option, } #[cfg(feature = "olap")] @@ -188,6 +197,7 @@ impl AuthToken { role_id: String, settings: &Settings, org_id: id_type::OrganizationId, + profile_id: Option, ) -> UserResult { let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS); let exp = jwt::generate_exp(exp_duration)?.as_secs(); @@ -197,6 +207,7 @@ impl AuthToken { role_id, exp, org_id, + profile_id, }; jwt::generate_jwt(&token_payload, settings).await } @@ -208,6 +219,7 @@ pub struct UserFromToken { pub merchant_id: id_type::MerchantId, pub role_id: String, pub org_id: id_type::OrganizationId, + pub profile_id: Option, } pub struct UserIdFromAuth { @@ -376,6 +388,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, + profile_id: None, }; Ok(( auth.clone(), @@ -512,6 +525,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, + profile_id: None, }; Ok(auth) @@ -735,6 +749,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, + profile_id: None, }; Ok(( auth.clone(), @@ -852,6 +867,61 @@ where merchant_id: payload.merchant_id.clone(), org_id: payload.org_id, role_id: payload.role_id, + profile_id: payload.profile_id, + }, + AuthenticationType::MerchantJwt { + merchant_id: payload.merchant_id, + user_id: Some(payload.user_id), + }, + )) + } +} + +#[cfg(feature = "olap")] +#[async_trait] +impl AuthenticateAndFetch for JWTAuth +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationDataWithMultipleProfiles, AuthenticationType)> { + let payload = parse_jwt_payload::(request_headers, state).await?; + if payload.check_in_blacklist(state).await? { + return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); + } + + let permissions = authorization::get_permissions(state, &payload).await?; + authorization::check_authorization(&self.0, &permissions)?; + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .change_context(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant key store for the merchant id")?; + + let merchant = state + .store() + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) + .await + .change_context(errors::ApiErrorResponse::InvalidJwtToken)?; + + Ok(( + AuthenticationDataWithMultipleProfiles { + key_store, + merchant_account: merchant, + profile_id_list: None, }, AuthenticationType::MerchantJwt { merchant_id: payload.merchant_id, @@ -1020,6 +1090,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, + profile_id: payload.profile_id, }; Ok(( auth.clone(), @@ -1075,6 +1146,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, + profile_id: payload.profile_id, }; Ok(( (auth.clone(), payload.user_id.clone()), @@ -1110,6 +1182,7 @@ where merchant_id: payload.merchant_id.clone(), org_id: payload.org_id, role_id: payload.role_id, + profile_id: payload.profile_id, }, AuthenticationType::MerchantJwt { merchant_id: payload.merchant_id, @@ -1175,6 +1248,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, + profile_id: payload.profile_id, }; Ok(( auth.clone(), diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 1a2ac3fa0545..41fbc96f6d2f 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -1144,8 +1144,12 @@ impl SignInWithSingleRoleStrategy { self, state: &SessionState, ) -> UserResult { - let token = - utils::user::generate_jwt_auth_token(state, &self.user, &self.user_role).await?; + let token = utils::user::generate_jwt_auth_token_without_profile( + state, + &self.user, + &self.user_role, + ) + .await?; utils::user_role::set_role_permissions_in_cache_by_user_role(state, &self.user_role).await; let dashboard_entry_response = diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index cffdd05448a7..92f1d4ba5baf 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -101,7 +101,7 @@ impl JWTFlow { Ok(true) } - pub async fn generate_jwt( + pub async fn generate_jwt_without_profile( self, state: &SessionState, next_flow: &NextFlow, @@ -119,6 +119,7 @@ impl JWTFlow { .org_id .clone() .ok_or(report!(UserErrors::InternalServerError))?, + None, ) .await .map(|token| token.into()) @@ -293,7 +294,9 @@ impl NextFlow { utils::user_role::set_role_permissions_in_cache_by_user_role(state, &user_role) .await; - jwt_flow.generate_jwt(state, self, &user_role).await + jwt_flow + .generate_jwt_without_profile(state, self, &user_role) + .await } } } @@ -313,7 +316,9 @@ impl NextFlow { utils::user_role::set_role_permissions_in_cache_by_user_role(state, user_role) .await; - jwt_flow.generate_jwt(state, self, user_role).await + jwt_flow + .generate_jwt_without_profile(state, self, user_role) + .await } } } diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 72d2b83c6b45..2dad55c4b89d 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -81,7 +81,7 @@ impl UserFromToken { } } -pub async fn generate_jwt_auth_token( +pub async fn generate_jwt_auth_token_without_profile( state: &SessionState, user: &UserFromStorage, user_role: &UserRole, @@ -102,6 +102,7 @@ pub async fn generate_jwt_auth_token( .ok_or(report!(UserErrors::InternalServerError)) .attach_printable("org_id not found for user_role")? .clone(), + None, ) .await?; Ok(Secret::new(token)) @@ -113,6 +114,7 @@ pub async fn generate_jwt_auth_token_with_custom_role_attributes( merchant_id: id_type::MerchantId, org_id: id_type::OrganizationId, role_id: String, + profile_id: Option, ) -> UserResult> { let token = AuthToken::new_token( user.get_user_id().to_string(), @@ -120,6 +122,7 @@ pub async fn generate_jwt_auth_token_with_custom_role_attributes( role_id, &state.conf, org_id, + profile_id, ) .await?; Ok(Secret::new(token)) From 537630f00482939d4c0b49c643dee3763fe0e046 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com> Date: Thu, 1 Aug 2024 19:14:21 +0530 Subject: [PATCH 7/7] feat(business_profile): introduce domain models for business profile v1 and v2 APIs (#5497) --- crates/api_models/Cargo.toml | 7 +- crates/common_enums/src/enums.rs | 21 + crates/diesel_models/Cargo.toml | 3 +- crates/diesel_models/src/business_profile.rs | 625 +++++++++++++- crates/diesel_models/src/enums.rs | 1 + .../src/query/business_profile.rs | 11 +- crates/diesel_models/src/schema_v2.rs | 13 +- crates/hyperswitch_domain_models/Cargo.toml | 1 + .../src/business_profile.rs | 763 ++++++++++++++++++ crates/hyperswitch_domain_models/src/lib.rs | 1 + crates/router/Cargo.toml | 1 + crates/router/src/core/admin.rs | 1 - crates/router/src/core/routing/helpers.rs | 24 +- justfile | 10 +- scripts/ci-checks.sh | 2 +- .../down.sql | 18 + .../up.sql | 17 + 17 files changed, 1458 insertions(+), 61 deletions(-) create mode 100644 crates/hyperswitch_domain_models/src/business_profile.rs create mode 100644 v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql create mode 100644 v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql diff --git a/crates/api_models/Cargo.toml b/crates/api_models/Cargo.toml index b6b6f219bd88..f829d2e48fec 100644 --- a/crates/api_models/Cargo.toml +++ b/crates/api_models/Cargo.toml @@ -17,13 +17,14 @@ frm = [] olap = [] openapi = ["common_enums/openapi", "olap", "recon", "dummy_connector", "olap"] recon = [] -v1 =[] +v1 = [] v2 = [] -routing_v2 = [] -merchant_connector_account_v2 = [] +business_profile_v2 = [] customer_v2 = [] merchant_account_v2 = [] +merchant_connector_account_v2 = [] payment_v2 = [] +routing_v2 = [] [dependencies] actix-web = { version = "4.5.1", optional = true } diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 985f7451c285..6ec400ec81c4 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3054,3 +3054,24 @@ pub enum PayoutRetryType { SingleConnector, MultiConnector, } + +#[derive( + Clone, + Copy, + Debug, + Eq, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumString, + ToSchema, + Hash, +)] +#[router_derive::diesel_enum(storage_type = "db_enum")] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum OrderFulfillmentTimeOrigin { + Create, + Confirm, +} diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index d021ccfff373..6b63f76cdaae 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -8,10 +8,11 @@ readme = "README.md" license.workspace = true [features] -default = ["kv_store", "v1"] +default = ["kv_store", "v1"] kv_store = [] v1 =[] v2 = [] +business_profile_v2 = [] customer_v2 = [] merchant_account_v2 = [] merchant_connector_account_v2 = [] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 30f6780e3600..cc4dbe6b605e 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -1,8 +1,25 @@ +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +use common_enums::OrderFulfillmentTimeOrigin; use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] use crate::schema::business_profile; +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +use crate::schema_v2::business_profile; +/// Note: The order of fields in the struct is important. +/// This should be in the same order as the fields in the schema.rs file, otherwise the code will +/// not compile +/// If two adjacent columns have the same type, then the compiler will not throw any error, but the +/// fields read / written will be interchanged +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] #[derive( Clone, Debug, @@ -46,6 +63,10 @@ pub struct BusinessProfile { pub outgoing_webhook_custom_http_headers: Option, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile, primary_key(profile_id))] pub struct BusinessProfileNew { @@ -80,11 +101,15 @@ pub struct BusinessProfileNew { pub outgoing_webhook_custom_http_headers: Option, } -#[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] +#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile)] pub struct BusinessProfileUpdateInternal { pub profile_name: Option, - pub modified_at: Option, + pub modified_at: time::PrimitiveDateTime, pub return_url: Option, pub enable_payment_response_hash: Option, pub payment_response_hash_key: Option, @@ -111,11 +136,14 @@ pub struct BusinessProfileUpdateInternal { pub outgoing_webhook_custom_http_headers: Option, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum BusinessProfileUpdate { Update { profile_name: Option, - modified_at: Option, return_url: Option, enable_payment_response_hash: Option, payment_response_hash_key: Option, @@ -139,6 +167,10 @@ pub enum BusinessProfileUpdate { is_connector_agnostic_mit_enabled: Option, outgoing_webhook_custom_http_headers: Option, }, + RoutingAlgorithmUpdate { + routing_algorithm: Option, + payout_routing_algorithm: Option, + }, ExtendedCardInfoUpdate { is_extended_card_info_enabled: Option, }, @@ -147,12 +179,17 @@ pub enum BusinessProfileUpdate { }, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] impl From for BusinessProfileUpdateInternal { fn from(business_profile_update: BusinessProfileUpdate) -> Self { + let now = common_utils::date_time::now(); + match business_profile_update { BusinessProfileUpdate::Update { profile_name, - modified_at, return_url, enable_payment_response_hash, payment_response_hash_key, @@ -177,7 +214,7 @@ impl From for BusinessProfileUpdateInternal { outgoing_webhook_custom_http_headers, } => Self { profile_name, - modified_at, + modified_at: now, return_url, enable_payment_response_hash, payment_response_hash_key, @@ -194,30 +231,113 @@ impl From for BusinessProfileUpdateInternal { session_expiry, authentication_connector_details, payout_link_config, + is_extended_card_info_enabled: None, extended_card_info_config, use_billing_as_payment_method_billing, collect_shipping_details_from_wallet_connector, collect_billing_details_from_wallet_connector, is_connector_agnostic_mit_enabled, outgoing_webhook_custom_http_headers, - ..Default::default() + }, + BusinessProfileUpdate::RoutingAlgorithmUpdate { + routing_algorithm, + payout_routing_algorithm, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, }, BusinessProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm: None, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, is_extended_card_info_enabled, - ..Default::default() + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, }, BusinessProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm: None, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, is_connector_agnostic_mit_enabled, - ..Default::default() + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, }, } } } +// This is being used only in the `BusinessProfileInterface` implementation for `MockDb`. +// This can be removed once the `BusinessProfileInterface` trait has been updated to use the domain +// model instead. +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] impl From for BusinessProfile { fn from(new: BusinessProfileNew) -> Self { Self { @@ -255,11 +375,15 @@ impl From for BusinessProfile { } } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] impl BusinessProfileUpdate { pub fn apply_changeset(self, source: BusinessProfile) -> BusinessProfile { let BusinessProfileUpdateInternal { profile_name, - modified_at: _, + modified_at, return_url, enable_payment_response_hash, payment_response_hash_key, @@ -285,34 +409,497 @@ impl BusinessProfileUpdate { outgoing_webhook_custom_http_headers, } = self.into(); BusinessProfile { + profile_id: source.profile_id, + merchant_id: source.merchant_id, profile_name: profile_name.unwrap_or(source.profile_name), - modified_at: common_utils::date_time::now(), - return_url, + created_at: source.created_at, + modified_at, + return_url: return_url.or(source.return_url), enable_payment_response_hash: enable_payment_response_hash .unwrap_or(source.enable_payment_response_hash), - payment_response_hash_key, + payment_response_hash_key: payment_response_hash_key + .or(source.payment_response_hash_key), redirect_to_merchant_with_http_post: redirect_to_merchant_with_http_post .unwrap_or(source.redirect_to_merchant_with_http_post), + webhook_details: webhook_details.or(source.webhook_details), + metadata: metadata.or(source.metadata), + routing_algorithm: routing_algorithm.or(source.routing_algorithm), + intent_fulfillment_time: intent_fulfillment_time.or(source.intent_fulfillment_time), + frm_routing_algorithm: frm_routing_algorithm.or(source.frm_routing_algorithm), + payout_routing_algorithm: payout_routing_algorithm.or(source.payout_routing_algorithm), + is_recon_enabled: is_recon_enabled.unwrap_or(source.is_recon_enabled), + applepay_verified_domains: applepay_verified_domains + .or(source.applepay_verified_domains), + payment_link_config: payment_link_config.or(source.payment_link_config), + session_expiry: session_expiry.or(source.session_expiry), + authentication_connector_details: authentication_connector_details + .or(source.authentication_connector_details), + payout_link_config: payout_link_config.or(source.payout_link_config), + is_extended_card_info_enabled: is_extended_card_info_enabled + .or(source.is_extended_card_info_enabled), + is_connector_agnostic_mit_enabled: is_connector_agnostic_mit_enabled + .or(source.is_connector_agnostic_mit_enabled), + extended_card_info_config: extended_card_info_config + .or(source.extended_card_info_config), + use_billing_as_payment_method_billing: use_billing_as_payment_method_billing + .or(source.use_billing_as_payment_method_billing), + collect_shipping_details_from_wallet_connector: + collect_shipping_details_from_wallet_connector + .or(source.collect_shipping_details_from_wallet_connector), + collect_billing_details_from_wallet_connector: + collect_billing_details_from_wallet_connector + .or(source.collect_billing_details_from_wallet_connector), + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers + .or(source.outgoing_webhook_custom_http_headers), + } + } +} + +/// Note: The order of fields in the struct is important. +/// This should be in the same order as the fields in the schema.rs file, otherwise the code will +/// not compile +/// If two adjacent columns have the same type, then the compiler will not throw any error, but the +/// fields read / written will be interchanged +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[derive( + Clone, + Debug, + serde::Deserialize, + serde::Serialize, + Identifiable, + Queryable, + Selectable, + router_derive::DebugAsDisplay, +)] +#[diesel(table_name = business_profile, primary_key(profile_id), check_for_backend(diesel::pg::Pg))] +pub struct BusinessProfile { + pub profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub profile_name: String, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub return_url: Option, + pub enable_payment_response_hash: bool, + pub payment_response_hash_key: Option, + pub redirect_to_merchant_with_http_post: bool, + pub webhook_details: Option, + pub metadata: Option, + pub is_recon_enabled: bool, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub applepay_verified_domains: Option>, + pub payment_link_config: Option, + pub session_expiry: Option, + pub authentication_connector_details: Option, + pub payout_link_config: Option, + pub is_extended_card_info_enabled: Option, + pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, + pub collect_shipping_details_from_wallet_connector: Option, + pub collect_billing_details_from_wallet_connector: Option, + pub outgoing_webhook_custom_http_headers: Option, + pub routing_algorithm_id: Option, + pub order_fulfillment_time: Option, + pub order_fulfillment_time_origin: Option, + pub frm_routing_algorithm_id: Option, + pub payout_routing_algorithm_id: Option, + pub default_fallback_routing: Option, +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] +#[diesel(table_name = business_profile, primary_key(profile_id))] +pub struct BusinessProfileNew { + pub profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub profile_name: String, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub return_url: Option, + pub enable_payment_response_hash: bool, + pub payment_response_hash_key: Option, + pub redirect_to_merchant_with_http_post: bool, + pub webhook_details: Option, + pub metadata: Option, + pub is_recon_enabled: bool, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub applepay_verified_domains: Option>, + pub payment_link_config: Option, + pub session_expiry: Option, + pub authentication_connector_details: Option, + pub payout_link_config: Option, + pub is_extended_card_info_enabled: Option, + pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, + pub collect_shipping_details_from_wallet_connector: Option, + pub collect_billing_details_from_wallet_connector: Option, + pub outgoing_webhook_custom_http_headers: Option, + pub routing_algorithm_id: Option, + pub order_fulfillment_time: Option, + pub order_fulfillment_time_origin: Option, + pub frm_routing_algorithm_id: Option, + pub payout_routing_algorithm_id: Option, + pub default_fallback_routing: Option, +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] +#[diesel(table_name = business_profile)] +pub struct BusinessProfileUpdateInternal { + pub profile_name: Option, + pub modified_at: time::PrimitiveDateTime, + pub return_url: Option, + pub enable_payment_response_hash: Option, + pub payment_response_hash_key: Option, + pub redirect_to_merchant_with_http_post: Option, + pub webhook_details: Option, + pub metadata: Option, + pub is_recon_enabled: Option, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub applepay_verified_domains: Option>, + pub payment_link_config: Option, + pub session_expiry: Option, + pub authentication_connector_details: Option, + pub payout_link_config: Option, + pub is_extended_card_info_enabled: Option, + pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, + pub collect_shipping_details_from_wallet_connector: Option, + pub collect_billing_details_from_wallet_connector: Option, + pub outgoing_webhook_custom_http_headers: Option, + pub routing_algorithm_id: Option, + pub order_fulfillment_time: Option, + pub order_fulfillment_time_origin: Option, + pub frm_routing_algorithm_id: Option, + pub payout_routing_algorithm_id: Option, + pub default_fallback_routing: Option, +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum BusinessProfileUpdate { + Update { + profile_name: Option, + return_url: Option, + enable_payment_response_hash: Option, + payment_response_hash_key: Option, + redirect_to_merchant_with_http_post: Option, + webhook_details: Option, + metadata: Option, + is_recon_enabled: Option, + applepay_verified_domains: Option>, + payment_link_config: Option, + session_expiry: Option, + authentication_connector_details: Option, + payout_link_config: Option, + extended_card_info_config: Option, + use_billing_as_payment_method_billing: Option, + collect_shipping_details_from_wallet_connector: Option, + collect_billing_details_from_wallet_connector: Option, + is_connector_agnostic_mit_enabled: Option, + outgoing_webhook_custom_http_headers: Option, + routing_algorithm_id: Option, + order_fulfillment_time: Option, + order_fulfillment_time_origin: Option, + frm_routing_algorithm_id: Option, + payout_routing_algorithm_id: Option, + default_fallback_routing: Option, + }, + RoutingAlgorithmUpdate { + routing_algorithm_id: Option, + payout_routing_algorithm_id: Option, + }, + ExtendedCardInfoUpdate { + is_extended_card_info_enabled: Option, + }, + ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled: Option, + }, +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +impl From for BusinessProfileUpdateInternal { + fn from(business_profile_update: BusinessProfileUpdate) -> Self { + let now = common_utils::date_time::now(); + + match business_profile_update { + BusinessProfileUpdate::Update { + profile_name, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + payout_link_config, + extended_card_info_config, + use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector, + is_connector_agnostic_mit_enabled, + outgoing_webhook_custom_http_headers, + routing_algorithm_id, + order_fulfillment_time, + order_fulfillment_time_origin, + frm_routing_algorithm_id, + payout_routing_algorithm_id, + default_fallback_routing, + } => Self { + profile_name, + modified_at: now, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + payout_link_config, + is_extended_card_info_enabled: None, + extended_card_info_config, + use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector, + is_connector_agnostic_mit_enabled, + outgoing_webhook_custom_http_headers, + routing_algorithm_id, + order_fulfillment_time, + order_fulfillment_time_origin, + frm_routing_algorithm_id, + payout_routing_algorithm_id, + default_fallback_routing, + }, + BusinessProfileUpdate::RoutingAlgorithmUpdate { + routing_algorithm_id, + payout_routing_algorithm_id, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id, + default_fallback_routing: None, + }, + BusinessProfileUpdate::ExtendedCardInfoUpdate { + is_extended_card_info_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id: None, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id: None, + default_fallback_routing: None, + }, + BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id: None, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id: None, + default_fallback_routing: None, + }, + } + } +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +impl BusinessProfileUpdate { + pub fn apply_changeset(self, source: BusinessProfile) -> BusinessProfile { + let BusinessProfileUpdateInternal { + profile_name, + modified_at, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, webhook_details, metadata, - routing_algorithm, - intent_fulfillment_time, - frm_routing_algorithm, - payout_routing_algorithm, - is_recon_enabled: is_recon_enabled.unwrap_or(source.is_recon_enabled), + is_recon_enabled, applepay_verified_domains, payment_link_config, session_expiry, authentication_connector_details, payout_link_config, is_extended_card_info_enabled, - is_connector_agnostic_mit_enabled, extended_card_info_config, + is_connector_agnostic_mit_enabled, use_billing_as_payment_method_billing, collect_shipping_details_from_wallet_connector, collect_billing_details_from_wallet_connector, outgoing_webhook_custom_http_headers, - ..source + routing_algorithm_id, + order_fulfillment_time, + order_fulfillment_time_origin, + frm_routing_algorithm_id, + payout_routing_algorithm_id, + default_fallback_routing, + } = self.into(); + BusinessProfile { + profile_id: source.profile_id, + merchant_id: source.merchant_id, + profile_name: profile_name.unwrap_or(source.profile_name), + created_at: source.created_at, + modified_at, + return_url: return_url.or(source.return_url), + enable_payment_response_hash: enable_payment_response_hash + .unwrap_or(source.enable_payment_response_hash), + payment_response_hash_key: payment_response_hash_key + .or(source.payment_response_hash_key), + redirect_to_merchant_with_http_post: redirect_to_merchant_with_http_post + .unwrap_or(source.redirect_to_merchant_with_http_post), + webhook_details: webhook_details.or(source.webhook_details), + metadata: metadata.or(source.metadata), + is_recon_enabled: is_recon_enabled.unwrap_or(source.is_recon_enabled), + applepay_verified_domains: applepay_verified_domains + .or(source.applepay_verified_domains), + payment_link_config: payment_link_config.or(source.payment_link_config), + session_expiry: session_expiry.or(source.session_expiry), + authentication_connector_details: authentication_connector_details + .or(source.authentication_connector_details), + payout_link_config: payout_link_config.or(source.payout_link_config), + is_extended_card_info_enabled: is_extended_card_info_enabled + .or(source.is_extended_card_info_enabled), + is_connector_agnostic_mit_enabled: is_connector_agnostic_mit_enabled + .or(source.is_connector_agnostic_mit_enabled), + extended_card_info_config: extended_card_info_config + .or(source.extended_card_info_config), + use_billing_as_payment_method_billing: use_billing_as_payment_method_billing + .or(source.use_billing_as_payment_method_billing), + collect_shipping_details_from_wallet_connector: + collect_shipping_details_from_wallet_connector + .or(source.collect_shipping_details_from_wallet_connector), + collect_billing_details_from_wallet_connector: + collect_billing_details_from_wallet_connector + .or(source.collect_billing_details_from_wallet_connector), + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers + .or(source.outgoing_webhook_custom_http_headers), + routing_algorithm_id: routing_algorithm_id.or(source.routing_algorithm_id), + order_fulfillment_time: order_fulfillment_time.or(source.order_fulfillment_time), + order_fulfillment_time_origin: order_fulfillment_time_origin + .or(source.order_fulfillment_time_origin), + frm_routing_algorithm_id: frm_routing_algorithm_id.or(source.frm_routing_algorithm_id), + payout_routing_algorithm_id: payout_routing_algorithm_id + .or(source.payout_routing_algorithm_id), + default_fallback_routing: default_fallback_routing.or(source.default_fallback_routing), + } + } +} + +// This is being used only in the `BusinessProfileInterface` implementation for `MockDb`. +// This can be removed once the `BusinessProfileInterface` trait has been updated to use the domain +// model instead. +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +impl From for BusinessProfile { + fn from(new: BusinessProfileNew) -> Self { + Self { + profile_id: new.profile_id, + merchant_id: new.merchant_id, + profile_name: new.profile_name, + created_at: new.created_at, + modified_at: new.modified_at, + return_url: new.return_url, + enable_payment_response_hash: new.enable_payment_response_hash, + payment_response_hash_key: new.payment_response_hash_key, + redirect_to_merchant_with_http_post: new.redirect_to_merchant_with_http_post, + webhook_details: new.webhook_details, + metadata: new.metadata, + is_recon_enabled: new.is_recon_enabled, + applepay_verified_domains: new.applepay_verified_domains, + payment_link_config: new.payment_link_config, + session_expiry: new.session_expiry, + authentication_connector_details: new.authentication_connector_details, + payout_link_config: new.payout_link_config, + is_connector_agnostic_mit_enabled: new.is_connector_agnostic_mit_enabled, + is_extended_card_info_enabled: new.is_extended_card_info_enabled, + extended_card_info_config: new.extended_card_info_config, + use_billing_as_payment_method_billing: new.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: new + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: new + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: new.outgoing_webhook_custom_http_headers, + routing_algorithm_id: new.routing_algorithm_id, + order_fulfillment_time: new.order_fulfillment_time, + order_fulfillment_time_origin: new.order_fulfillment_time_origin, + frm_routing_algorithm_id: new.frm_routing_algorithm_id, + payout_routing_algorithm_id: new.payout_routing_algorithm_id, + default_fallback_routing: new.default_fallback_routing, } } } diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index f0059994cb67..55bfa9f2f231 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -12,6 +12,7 @@ pub mod diesel_exports { DbFutureUsage as FutureUsage, DbGenericLinkType as GenericLinkType, DbIntentStatus as IntentStatus, DbMandateStatus as MandateStatus, DbMandateType as MandateType, DbMerchantStorageScheme as MerchantStorageScheme, + DbOrderFulfillmentTimeOrigin as OrderFulfillmentTimeOrigin, DbPaymentMethodIssuerCode as PaymentMethodIssuerCode, DbPaymentSource as PaymentSource, DbPaymentType as PaymentType, DbPayoutStatus as PayoutStatus, DbPayoutType as PayoutType, DbProcessTrackerStatus as ProcessTrackerStatus, DbReconStatus as ReconStatus, diff --git a/crates/diesel_models/src/query/business_profile.rs b/crates/diesel_models/src/query/business_profile.rs index e716e45c6504..1b8e54e0180b 100644 --- a/crates/diesel_models/src/query/business_profile.rs +++ b/crates/diesel_models/src/query/business_profile.rs @@ -1,13 +1,18 @@ use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Table}; use super::generics; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] +use crate::schema::business_profile::dsl; +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +use crate::schema_v2::business_profile::dsl; use crate::{ business_profile::{ BusinessProfile, BusinessProfileNew, BusinessProfileUpdate, BusinessProfileUpdateInternal, }, - errors, - schema::business_profile::dsl, - PgPooledConn, StorageResult, + errors, PgPooledConn, StorageResult, }; impl BusinessProfileNew { diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 648e6e44652e..23f9dff31ea1 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -188,10 +188,6 @@ diesel::table! { redirect_to_merchant_with_http_post -> Bool, webhook_details -> Nullable, metadata -> Nullable, - routing_algorithm -> Nullable, - intent_fulfillment_time -> Nullable, - frm_routing_algorithm -> Nullable, - payout_routing_algorithm -> Nullable, is_recon_enabled -> Bool, applepay_verified_domains -> Nullable>>, payment_link_config -> Nullable, @@ -205,6 +201,15 @@ diesel::table! { collect_shipping_details_from_wallet_connector -> Nullable, collect_billing_details_from_wallet_connector -> Nullable, outgoing_webhook_custom_http_headers -> Nullable, + #[max_length = 64] + routing_algorithm_id -> Nullable, + order_fulfillment_time -> Nullable, + order_fulfillment_time_origin -> Nullable, + #[max_length = 64] + frm_routing_algorithm_id -> Nullable, + #[max_length = 64] + payout_routing_algorithm_id -> Nullable, + default_fallback_routing -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index 662cfcff65d6..16ab18b68dff 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -15,6 +15,7 @@ payouts = ["api_models/payouts"] frm = ["api_models/frm"] v2 = ["api_models/v2", "diesel_models/v2"] v1 = ["api_models/v1", "diesel_models/v1"] +business_profile_v2 = ["api_models/business_profile_v2", "diesel_models/business_profile_v2"] customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2"] merchant_account_v2 = ["api_models/merchant_account_v2", "diesel_models/merchant_account_v2"] merchant_connector_account_v2 = ["api_models/merchant_connector_account_v2", "diesel_models/merchant_connector_account_v2"] diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs new file mode 100644 index 000000000000..e6b2bcbe5b11 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -0,0 +1,763 @@ +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +use common_enums::OrderFulfillmentTimeOrigin; +use common_utils::{ + crypto::OptionalEncryptableValue, + date_time, + encryption::Encryption, + errors::{CustomResult, ValidationError}, + pii, + types::keymanager, +}; +use diesel_models::business_profile::BusinessProfileUpdateInternal; +use error_stack::ResultExt; +use masking::{PeekInterface, Secret}; + +use crate::type_encryption::{decrypt_optional, AsyncLift}; + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] +#[derive(Clone, Debug)] +pub struct BusinessProfile { + pub profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub profile_name: String, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub return_url: Option, + pub enable_payment_response_hash: bool, + pub payment_response_hash_key: Option, + pub redirect_to_merchant_with_http_post: bool, + pub webhook_details: Option, + pub metadata: Option, + pub routing_algorithm: Option, + pub intent_fulfillment_time: Option, + pub frm_routing_algorithm: Option, + pub payout_routing_algorithm: Option, + pub is_recon_enabled: bool, + pub applepay_verified_domains: Option>, + pub payment_link_config: Option, + pub session_expiry: Option, + pub authentication_connector_details: Option, + pub payout_link_config: Option, + pub is_extended_card_info_enabled: Option, + pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, + pub collect_shipping_details_from_wallet_connector: Option, + pub collect_billing_details_from_wallet_connector: Option, + pub outgoing_webhook_custom_http_headers: OptionalEncryptableValue, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] +#[derive(Debug)] +pub enum BusinessProfileUpdate { + Update { + profile_name: Option, + return_url: Option, + enable_payment_response_hash: Option, + payment_response_hash_key: Option, + redirect_to_merchant_with_http_post: Option, + webhook_details: Option, + metadata: Option, + routing_algorithm: Option, + intent_fulfillment_time: Option, + frm_routing_algorithm: Option, + payout_routing_algorithm: Option, + is_recon_enabled: Option, + applepay_verified_domains: Option>, + payment_link_config: Option, + session_expiry: Option, + authentication_connector_details: Option, + payout_link_config: Option, + extended_card_info_config: Option, + use_billing_as_payment_method_billing: Option, + collect_shipping_details_from_wallet_connector: Option, + collect_billing_details_from_wallet_connector: Option, + is_connector_agnostic_mit_enabled: Option, + outgoing_webhook_custom_http_headers: OptionalEncryptableValue, + }, + RoutingAlgorithmUpdate { + routing_algorithm: Option, + payout_routing_algorithm: Option, + }, + ExtendedCardInfoUpdate { + is_extended_card_info_enabled: Option, + }, + ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled: Option, + }, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] +impl From for BusinessProfileUpdateInternal { + fn from(business_profile_update: BusinessProfileUpdate) -> Self { + let now = date_time::now(); + + match business_profile_update { + BusinessProfileUpdate::Update { + profile_name, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + routing_algorithm, + intent_fulfillment_time, + frm_routing_algorithm, + payout_routing_algorithm, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + payout_link_config, + extended_card_info_config, + use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector, + is_connector_agnostic_mit_enabled, + outgoing_webhook_custom_http_headers, + } => Self { + profile_name, + modified_at: now, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + routing_algorithm, + intent_fulfillment_time, + frm_routing_algorithm, + payout_routing_algorithm, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + payout_link_config, + is_extended_card_info_enabled: None, + extended_card_info_config, + is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers + .map(Encryption::from), + }, + BusinessProfileUpdate::RoutingAlgorithmUpdate { + routing_algorithm, + payout_routing_algorithm, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + }, + BusinessProfileUpdate::ExtendedCardInfoUpdate { + is_extended_card_info_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm: None, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + }, + BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm: None, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + }, + } + } +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "business_profile_v2") +))] +#[async_trait::async_trait] +impl super::behaviour::Conversion for BusinessProfile { + type DstType = diesel_models::business_profile::BusinessProfile; + type NewDstType = diesel_models::business_profile::BusinessProfileNew; + + async fn convert(self) -> CustomResult { + Ok(diesel_models::business_profile::BusinessProfile { + profile_id: self.profile_id, + merchant_id: self.merchant_id, + profile_name: self.profile_name, + created_at: self.created_at, + modified_at: self.modified_at, + return_url: self.return_url, + enable_payment_response_hash: self.enable_payment_response_hash, + payment_response_hash_key: self.payment_response_hash_key, + redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post, + webhook_details: self.webhook_details, + metadata: self.metadata, + routing_algorithm: self.routing_algorithm, + intent_fulfillment_time: self.intent_fulfillment_time, + frm_routing_algorithm: self.frm_routing_algorithm, + payout_routing_algorithm: self.payout_routing_algorithm, + is_recon_enabled: self.is_recon_enabled, + applepay_verified_domains: self.applepay_verified_domains, + payment_link_config: self.payment_link_config, + session_expiry: self.session_expiry, + authentication_connector_details: self.authentication_connector_details, + payout_link_config: self.payout_link_config, + is_extended_card_info_enabled: self.is_extended_card_info_enabled, + extended_card_info_config: self.extended_card_info_config, + is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: self + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: self + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: self + .outgoing_webhook_custom_http_headers + .map(Encryption::from), + }) + } + + async fn convert_back( + state: &keymanager::KeyManagerState, + item: Self::DstType, + key: &Secret>, + key_manager_identifier: keymanager::Identifier, + ) -> CustomResult + where + Self: Sized, + { + async { + Ok::>(Self { + profile_id: item.profile_id, + merchant_id: item.merchant_id, + profile_name: item.profile_name, + created_at: item.created_at, + modified_at: item.modified_at, + return_url: item.return_url, + enable_payment_response_hash: item.enable_payment_response_hash, + payment_response_hash_key: item.payment_response_hash_key, + redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post, + webhook_details: item.webhook_details, + metadata: item.metadata, + routing_algorithm: item.routing_algorithm, + intent_fulfillment_time: item.intent_fulfillment_time, + frm_routing_algorithm: item.frm_routing_algorithm, + payout_routing_algorithm: item.payout_routing_algorithm, + is_recon_enabled: item.is_recon_enabled, + applepay_verified_domains: item.applepay_verified_domains, + payment_link_config: item.payment_link_config, + session_expiry: item.session_expiry, + authentication_connector_details: item.authentication_connector_details, + payout_link_config: item.payout_link_config, + is_extended_card_info_enabled: item.is_extended_card_info_enabled, + extended_card_info_config: item.extended_card_info_config, + is_connector_agnostic_mit_enabled: item.is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: item.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: item + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: item + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: item + .outgoing_webhook_custom_http_headers + .async_lift(|inner| { + decrypt_optional(state, inner, key_manager_identifier.clone(), key.peek()) + }) + .await?, + }) + } + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting business profile data".to_string(), + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(diesel_models::business_profile::BusinessProfileNew { + profile_id: self.profile_id, + merchant_id: self.merchant_id, + profile_name: self.profile_name, + created_at: self.created_at, + modified_at: self.modified_at, + return_url: self.return_url, + enable_payment_response_hash: self.enable_payment_response_hash, + payment_response_hash_key: self.payment_response_hash_key, + redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post, + webhook_details: self.webhook_details, + metadata: self.metadata, + routing_algorithm: self.routing_algorithm, + intent_fulfillment_time: self.intent_fulfillment_time, + frm_routing_algorithm: self.frm_routing_algorithm, + payout_routing_algorithm: self.payout_routing_algorithm, + is_recon_enabled: self.is_recon_enabled, + applepay_verified_domains: self.applepay_verified_domains, + payment_link_config: self.payment_link_config, + session_expiry: self.session_expiry, + authentication_connector_details: self.authentication_connector_details, + payout_link_config: self.payout_link_config, + is_extended_card_info_enabled: self.is_extended_card_info_enabled, + extended_card_info_config: self.extended_card_info_config, + is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: self + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: self + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: self + .outgoing_webhook_custom_http_headers + .map(Encryption::from), + }) + } +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[derive(Clone, Debug)] +pub struct BusinessProfile { + pub profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub profile_name: String, + pub created_at: time::PrimitiveDateTime, + pub modified_at: time::PrimitiveDateTime, + pub return_url: Option, + pub enable_payment_response_hash: bool, + pub payment_response_hash_key: Option, + pub redirect_to_merchant_with_http_post: bool, + pub webhook_details: Option, + pub metadata: Option, + pub is_recon_enabled: bool, + pub applepay_verified_domains: Option>, + pub payment_link_config: Option, + pub session_expiry: Option, + pub authentication_connector_details: Option, + pub payout_link_config: Option, + pub is_extended_card_info_enabled: Option, + pub extended_card_info_config: Option, + pub is_connector_agnostic_mit_enabled: Option, + pub use_billing_as_payment_method_billing: Option, + pub collect_shipping_details_from_wallet_connector: Option, + pub collect_billing_details_from_wallet_connector: Option, + pub outgoing_webhook_custom_http_headers: OptionalEncryptableValue, + pub routing_algorithm_id: Option, + pub order_fulfillment_time: Option, + pub order_fulfillment_time_origin: Option, + pub frm_routing_algorithm_id: Option, + pub payout_routing_algorithm_id: Option, + pub default_fallback_routing: Option, +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[derive(Debug)] +pub enum BusinessProfileUpdate { + Update { + profile_name: Option, + return_url: Option, + enable_payment_response_hash: Option, + payment_response_hash_key: Option, + redirect_to_merchant_with_http_post: Option, + webhook_details: Option, + metadata: Option, + is_recon_enabled: Option, + applepay_verified_domains: Option>, + payment_link_config: Option, + session_expiry: Option, + authentication_connector_details: Option, + payout_link_config: Option, + extended_card_info_config: Option, + use_billing_as_payment_method_billing: Option, + collect_shipping_details_from_wallet_connector: Option, + collect_billing_details_from_wallet_connector: Option, + is_connector_agnostic_mit_enabled: Option, + outgoing_webhook_custom_http_headers: OptionalEncryptableValue, + routing_algorithm_id: Option, + order_fulfillment_time: Option, + order_fulfillment_time_origin: Option, + frm_routing_algorithm_id: Option, + payout_routing_algorithm_id: Option, + default_fallback_routing: Option, + }, + RoutingAlgorithmUpdate { + routing_algorithm_id: Option, + payout_routing_algorithm_id: Option, + }, + ExtendedCardInfoUpdate { + is_extended_card_info_enabled: Option, + }, + ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled: Option, + }, +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +impl From for BusinessProfileUpdateInternal { + fn from(business_profile_update: BusinessProfileUpdate) -> Self { + let now = date_time::now(); + + match business_profile_update { + BusinessProfileUpdate::Update { + profile_name, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + payout_link_config, + extended_card_info_config, + use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector, + is_connector_agnostic_mit_enabled, + outgoing_webhook_custom_http_headers, + routing_algorithm_id, + order_fulfillment_time, + order_fulfillment_time_origin, + frm_routing_algorithm_id, + payout_routing_algorithm_id, + default_fallback_routing, + } => Self { + profile_name, + modified_at: now, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + payout_link_config, + is_extended_card_info_enabled: None, + extended_card_info_config, + is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers + .map(Encryption::from), + routing_algorithm_id, + order_fulfillment_time, + order_fulfillment_time_origin, + frm_routing_algorithm_id, + payout_routing_algorithm_id, + default_fallback_routing, + }, + BusinessProfileUpdate::RoutingAlgorithmUpdate { + routing_algorithm_id, + payout_routing_algorithm_id, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id, + default_fallback_routing: None, + }, + BusinessProfileUpdate::ExtendedCardInfoUpdate { + is_extended_card_info_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id: None, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id: None, + default_fallback_routing: None, + }, + BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + is_connector_agnostic_mit_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id: None, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id: None, + default_fallback_routing: None, + }, + } + } +} + +#[cfg(all(feature = "v2", feature = "business_profile_v2"))] +#[async_trait::async_trait] +impl super::behaviour::Conversion for BusinessProfile { + type DstType = diesel_models::business_profile::BusinessProfile; + type NewDstType = diesel_models::business_profile::BusinessProfileNew; + + async fn convert(self) -> CustomResult { + Ok(diesel_models::business_profile::BusinessProfile { + profile_id: self.profile_id, + merchant_id: self.merchant_id, + profile_name: self.profile_name, + created_at: self.created_at, + modified_at: self.modified_at, + return_url: self.return_url, + enable_payment_response_hash: self.enable_payment_response_hash, + payment_response_hash_key: self.payment_response_hash_key, + redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post, + webhook_details: self.webhook_details, + metadata: self.metadata, + is_recon_enabled: self.is_recon_enabled, + applepay_verified_domains: self.applepay_verified_domains, + payment_link_config: self.payment_link_config, + session_expiry: self.session_expiry, + authentication_connector_details: self.authentication_connector_details, + payout_link_config: self.payout_link_config, + is_extended_card_info_enabled: self.is_extended_card_info_enabled, + extended_card_info_config: self.extended_card_info_config, + is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: self + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: self + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: self + .outgoing_webhook_custom_http_headers + .map(Encryption::from), + routing_algorithm_id: self.routing_algorithm_id, + order_fulfillment_time: self.order_fulfillment_time, + order_fulfillment_time_origin: self.order_fulfillment_time_origin, + frm_routing_algorithm_id: self.frm_routing_algorithm_id, + payout_routing_algorithm_id: self.payout_routing_algorithm_id, + default_fallback_routing: self.default_fallback_routing, + }) + } + + async fn convert_back( + state: &keymanager::KeyManagerState, + item: Self::DstType, + key: &Secret>, + key_manager_identifier: keymanager::Identifier, + ) -> CustomResult + where + Self: Sized, + { + async { + Ok::>(Self { + profile_id: item.profile_id, + merchant_id: item.merchant_id, + profile_name: item.profile_name, + created_at: item.created_at, + modified_at: item.modified_at, + return_url: item.return_url, + enable_payment_response_hash: item.enable_payment_response_hash, + payment_response_hash_key: item.payment_response_hash_key, + redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post, + webhook_details: item.webhook_details, + metadata: item.metadata, + is_recon_enabled: item.is_recon_enabled, + applepay_verified_domains: item.applepay_verified_domains, + payment_link_config: item.payment_link_config, + session_expiry: item.session_expiry, + authentication_connector_details: item.authentication_connector_details, + payout_link_config: item.payout_link_config, + is_extended_card_info_enabled: item.is_extended_card_info_enabled, + extended_card_info_config: item.extended_card_info_config, + is_connector_agnostic_mit_enabled: item.is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: item.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: item + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: item + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: item + .outgoing_webhook_custom_http_headers + .async_lift(|inner| { + decrypt_optional(state, inner, key_manager_identifier.clone(), key.peek()) + }) + .await?, + routing_algorithm_id: item.routing_algorithm_id, + order_fulfillment_time: item.order_fulfillment_time, + order_fulfillment_time_origin: item.order_fulfillment_time_origin, + frm_routing_algorithm_id: item.frm_routing_algorithm_id, + payout_routing_algorithm_id: item.payout_routing_algorithm_id, + default_fallback_routing: item.default_fallback_routing, + }) + } + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting business profile data".to_string(), + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(diesel_models::business_profile::BusinessProfileNew { + profile_id: self.profile_id, + merchant_id: self.merchant_id, + profile_name: self.profile_name, + created_at: self.created_at, + modified_at: self.modified_at, + return_url: self.return_url, + enable_payment_response_hash: self.enable_payment_response_hash, + payment_response_hash_key: self.payment_response_hash_key, + redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post, + webhook_details: self.webhook_details, + metadata: self.metadata, + is_recon_enabled: self.is_recon_enabled, + applepay_verified_domains: self.applepay_verified_domains, + payment_link_config: self.payment_link_config, + session_expiry: self.session_expiry, + authentication_connector_details: self.authentication_connector_details, + payout_link_config: self.payout_link_config, + is_extended_card_info_enabled: self.is_extended_card_info_enabled, + extended_card_info_config: self.extended_card_info_config, + is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, + use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing, + collect_shipping_details_from_wallet_connector: self + .collect_shipping_details_from_wallet_connector, + collect_billing_details_from_wallet_connector: self + .collect_billing_details_from_wallet_connector, + outgoing_webhook_custom_http_headers: self + .outgoing_webhook_custom_http_headers + .map(Encryption::from), + routing_algorithm_id: self.routing_algorithm_id, + order_fulfillment_time: self.order_fulfillment_time, + order_fulfillment_time_origin: self.order_fulfillment_time_origin, + frm_routing_algorithm_id: self.frm_routing_algorithm_id, + payout_routing_algorithm_id: self.payout_routing_algorithm_id, + default_fallback_routing: self.default_fallback_routing, + }) + } +} diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index e7ad703f9274..cabeda2ccc24 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -1,5 +1,6 @@ pub mod api; pub mod behaviour; +pub mod business_profile; pub mod customer; pub mod errors; pub mod mandates; diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 398e9143cfcf..3e6f470d80eb 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -33,6 +33,7 @@ recon = ["email", "api_models/recon"] retry = [] v2 = ["api_models/v2", "diesel_models/v2", "hyperswitch_domain_models/v2", "storage_impl/v2", "kgraph_utils/v2"] v1 = ["api_models/v1", "diesel_models/v1", "hyperswitch_domain_models/v1", "storage_impl/v1", "hyperswitch_interfaces/v1", "kgraph_utils/v1"] +# business_profile_v2 = ["api_models/business_profile_v2", "diesel_models/business_profile_v2", "hyperswitch_domain_models/business_profile_v2"] customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2"] merchant_account_v2 = ["api_models/merchant_account_v2", "diesel_models/merchant_account_v2", "hyperswitch_domain_models/merchant_account_v2"] payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2"] diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 26afafd7ad5e..23d4b704cf3c 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3272,7 +3272,6 @@ pub async fn update_business_profile( let business_profile_update = storage::business_profile::BusinessProfileUpdate::Update { profile_name: request.profile_name, - modified_at: Some(date_time::now()), return_url: request.return_url.map(|return_url| return_url.to_string()), enable_payment_response_hash: request.enable_payment_response_hash, payment_response_hash_key: request.payment_response_hash_key, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 6ef668839507..f550c7e501fe 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -200,31 +200,9 @@ pub async fn update_business_profile_active_algorithm_ref( storage::enums::TransactionType::Payout => (None, Some(ref_val)), }; - let business_profile_update = BusinessProfileUpdate::Update { - profile_name: None, - return_url: None, - enable_payment_response_hash: None, - payment_response_hash_key: None, - redirect_to_merchant_with_http_post: None, - webhook_details: None, - metadata: None, + let business_profile_update = BusinessProfileUpdate::RoutingAlgorithmUpdate { routing_algorithm, - intent_fulfillment_time: None, - frm_routing_algorithm: None, payout_routing_algorithm, - applepay_verified_domains: None, - modified_at: None, - is_recon_enabled: None, - payment_link_config: None, - session_expiry: None, - authentication_connector_details: None, - payout_link_config: None, - extended_card_info_config: None, - use_billing_as_payment_method_billing: None, - collect_shipping_details_from_wallet_connector: None, - collect_billing_details_from_wallet_connector: None, - is_connector_agnostic_mit_enabled: None, - outgoing_webhook_custom_http_headers: None, }; db.update_business_profile_by_profile_id(current_business_profile, business_profile_update) diff --git a/justfile b/justfile index 228653a007a9..1823c1ec19e2 100644 --- a/justfile +++ b/justfile @@ -14,7 +14,7 @@ alias c := check # Check compilation of Rust code and catch common mistakes # We cannot run --all-features because v1 and v2 are mutually exclusive features -# Create a list of features by excluding certain features +# Create a list of features by excluding certain features clippy *FLAGS: #! /usr/bin/env bash set -euo pipefail @@ -120,9 +120,9 @@ euclid-wasm features='dummy_connector': precommit: fmt clippy # Check compilation of v2 feature on base dependencies -v2_intermediate_features := "merchant_account_v2,payment_v2,customer_v2" +v2_intermediate_features := "merchant_account_v2,payment_v2,customer_v2,business_profile_v2" hack_v2: - cargo hack clippy --feature-powerset --ignore-unknown-features --at-least-one-of "v2 " --include-features "v2" --include-features {{ v2_intermediate_features }} --package "hyperswitch_domain_models" --package "diesel_models" --package "api_models" + cargo hack clippy --feature-powerset --depth 2 --ignore-unknown-features --at-least-one-of "v2 " --include-features "v2" --include-features {{ v2_intermediate_features }} --package "hyperswitch_domain_models" --package "diesel_models" --package "api_models" cargo hack clippy --features "v2,payment_v2" -p storage_impl # Use the env variables if present, or fallback to default values @@ -174,7 +174,7 @@ migrate_v2 operation=default_operation *args='': set -euo pipefail EXIT_CODE=0 - just copy_migrations + just copy_migrations just run_migration {{ operation }} {{ resultant_dir }} {{ v2_config_file_dir }} {{ database_url }} {{ args }} || EXIT_CODE=$? just delete_dir_if_exists exit $EXIT_CODE @@ -186,5 +186,3 @@ resurrect: ci_hack: scripts/ci-checks.sh - - diff --git a/scripts/ci-checks.sh b/scripts/ci-checks.sh index 51089955728a..5a37d2f26218 100755 --- a/scripts/ci-checks.sh +++ b/scripts/ci-checks.sh @@ -77,7 +77,7 @@ crates_with_v1_feature="$( --null-input \ '$crates_with_features[] | select( IN("v1"; .features[])) # Select crates with `v1` feature - | { name, features: (.features - ["v1", "v2", "default", "payment_v2", "merchant_account_v2","customer_v2", "merchant_connector_account_v2"]) } # Remove specific features to generate feature combinations + | { name, features: ( .features | del( .[] | select( any( . ; test("(([a-z_]+)_)?v2|v1|default") ) ) ) ) } # Remove specific features to generate feature combinations | { name, features: ( .features | map([., "v1"] | join(",")) ) } # Add `v1` to remaining features and join them by comma | .name as $name | .features[] | { $name, features: . } # Expand nested features object to have package - features combinations | "\(.name) \(.features)" # Print out package name and features separated by space' diff --git a/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql new file mode 100644 index 000000000000..5645d4c73997 --- /dev/null +++ b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql @@ -0,0 +1,18 @@ +-- This adds back dropped columns in `up.sql`. +-- However, if the old columns were dropped, then we won't have data previously +-- stored in these columns. +ALTER TABLE business_profile + ADD COLUMN routing_algorithm JSON DEFAULT NULL, + ADD COLUMN intent_fulfillment_time BIGINT DEFAULT NULL, + ADD COLUMN frm_routing_algorithm JSONB DEFAULT NULL, + ADD COLUMN payout_routing_algorithm JSONB DEFAULT NULL; + +ALTER TABLE business_profile + DROP COLUMN routing_algorithm_id, + DROP COLUMN order_fulfillment_time, + DROP COLUMN order_fulfillment_time_origin, + DROP COLUMN frm_routing_algorithm_id, + DROP COLUMN payout_routing_algorithm_id, + DROP COLUMN default_fallback_routing; + +DROP TYPE "OrderFulfillmentTimeOrigin"; diff --git a/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql new file mode 100644 index 000000000000..f1cff1744681 --- /dev/null +++ b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql @@ -0,0 +1,17 @@ +CREATE TYPE "OrderFulfillmentTimeOrigin" AS ENUM ('create', 'confirm'); + +ALTER TABLE business_profile + ADD COLUMN routing_algorithm_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN order_fulfillment_time BIGINT DEFAULT NULL, + ADD COLUMN order_fulfillment_time_origin "OrderFulfillmentTimeOrigin" DEFAULT NULL, + ADD COLUMN frm_routing_algorithm_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN payout_routing_algorithm_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN default_fallback_routing JSONB DEFAULT NULL; + +-- Note: This query should not be run on higher environments as this leads to data loss. +-- The application will work fine even without these queries being run. +ALTER TABLE business_profile + DROP COLUMN routing_algorithm, + DROP COLUMN intent_fulfillment_time, + DROP COLUMN frm_routing_algorithm, + DROP COLUMN payout_routing_algorithm;