From d4b3dbc155906e8bc0fa1b14e73f45227395a32f Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Mon, 23 Dec 2024 15:37:16 +0530 Subject: [PATCH] feat(payments_v2): add payment method list endpoint (#6805) Co-authored-by: Sanchith Hegde Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- .gitignore | 4 +- .../profile--connector-accounts-list.mdx | 3 + .../merchant-account--profile-list.mdx | 3 + .../connector-account--create.mdx | 3 + .../connector-account--retrieve.mdx | 3 + .../connector-account--update.mdx | 3 + .../merchant-connector--delete.mdx | 3 + ...t-saved-payment-methods-for-a-customer.mdx | 3 + .../payments--payment-methods-list.mdx | 3 + api-reference-v2/mint.json | 11 +- api-reference-v2/openapi_spec.json | 528 +++++++++--------- crates/api_models/src/admin.rs | 131 +---- crates/api_models/src/events/payment.rs | 10 +- crates/api_models/src/payment_methods.rs | 37 +- crates/api_models/src/payments.rs | 19 + crates/common_types/Cargo.toml | 5 + crates/common_types/src/lib.rs | 1 + crates/common_types/src/payment_methods.rs | 126 +++++ .../src/merchant_connector_account.rs | 10 +- .../src/query/merchant_connector_account.rs | 18 + crates/diesel_models/src/schema_v2.rs | 2 +- crates/euclid_wasm/Cargo.toml | 3 +- crates/euclid_wasm/src/lib.rs | 18 +- .../src/merchant_connector_account.rs | 119 ++-- crates/kgraph_utils/benches/evaluation.rs | 47 +- crates/kgraph_utils/src/mca.rs | 119 ++-- crates/openapi/src/openapi_v2.rs | 9 +- crates/openapi/src/routes/payments.rs | 44 +- crates/router/src/core/admin.rs | 70 ++- .../router/src/core/payment_methods/cards.rs | 13 - crates/router/src/core/payments.rs | 3 + .../src/core/payments/payment_methods.rs | 214 +++++++ crates/router/src/core/payments/routing.rs | 163 +++--- crates/router/src/core/payout_link.rs | 2 +- crates/router/src/core/payouts.rs | 17 +- crates/router/src/core/payouts/helpers.rs | 1 + crates/router/src/db/kafka_store.rs | 17 + .../src/db/merchant_connector_account.rs | 55 ++ crates/router/src/routes/app.rs | 4 +- crates/router/src/routes/payment_methods.rs | 39 -- crates/router/src/routes/payments.rs | 48 ++ crates/router/src/types/transformers.rs | 102 +++- .../2024-08-28-081721_add_v2_columns/down.sql | 3 + .../2024-08-28-081721_add_v2_columns/up.sql | 4 + .../2024-10-08-081847_drop_v1_columns/up.sql | 2 +- 45 files changed, 1314 insertions(+), 728 deletions(-) create mode 100644 api-reference-v2/api-reference/business-profile/profile--connector-accounts-list.mdx create mode 100644 api-reference-v2/api-reference/merchant-account/merchant-account--profile-list.mdx create mode 100644 api-reference-v2/api-reference/merchant-connector-account/connector-account--create.mdx create mode 100644 api-reference-v2/api-reference/merchant-connector-account/connector-account--retrieve.mdx create mode 100644 api-reference-v2/api-reference/merchant-connector-account/connector-account--update.mdx create mode 100644 api-reference-v2/api-reference/merchant-connector-account/merchant-connector--delete.mdx create mode 100644 api-reference-v2/api-reference/payment-methods/list-saved-payment-methods-for-a-customer.mdx create mode 100644 api-reference-v2/api-reference/payments/payments--payment-methods-list.mdx create mode 100644 crates/common_types/src/payment_methods.rs create mode 100644 crates/router/src/core/payments/payment_methods.rs diff --git a/.gitignore b/.gitignore index 1209263db3c8..dcbeddf7adab 100644 --- a/.gitignore +++ b/.gitignore @@ -187,7 +187,7 @@ target/ ### VisualStudioCode ### .vscode/* -!.vscode/settings.json +.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json @@ -268,4 +268,4 @@ creds.json /.direnv # Nix services data -/data \ No newline at end of file +/data diff --git a/api-reference-v2/api-reference/business-profile/profile--connector-accounts-list.mdx b/api-reference-v2/api-reference/business-profile/profile--connector-accounts-list.mdx new file mode 100644 index 000000000000..55218be7c0b4 --- /dev/null +++ b/api-reference-v2/api-reference/business-profile/profile--connector-accounts-list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/profiles/{id}/connector-accounts +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-account/merchant-account--profile-list.mdx b/api-reference-v2/api-reference/merchant-account/merchant-account--profile-list.mdx new file mode 100644 index 000000000000..069bd602ddf4 --- /dev/null +++ b/api-reference-v2/api-reference/merchant-account/merchant-account--profile-list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/merchant-accounts/{id}/profiles +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-connector-account/connector-account--create.mdx b/api-reference-v2/api-reference/merchant-connector-account/connector-account--create.mdx new file mode 100644 index 000000000000..d672d6fa34dc --- /dev/null +++ b/api-reference-v2/api-reference/merchant-connector-account/connector-account--create.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v2/connector-accounts +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-connector-account/connector-account--retrieve.mdx b/api-reference-v2/api-reference/merchant-connector-account/connector-account--retrieve.mdx new file mode 100644 index 000000000000..dbd26b9b10b1 --- /dev/null +++ b/api-reference-v2/api-reference/merchant-connector-account/connector-account--retrieve.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/connector-accounts/{id} +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-connector-account/connector-account--update.mdx b/api-reference-v2/api-reference/merchant-connector-account/connector-account--update.mdx new file mode 100644 index 000000000000..fe864d538f8f --- /dev/null +++ b/api-reference-v2/api-reference/merchant-connector-account/connector-account--update.mdx @@ -0,0 +1,3 @@ +--- +openapi: put /v2/connector-accounts/{id} +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-connector-account/merchant-connector--delete.mdx b/api-reference-v2/api-reference/merchant-connector-account/merchant-connector--delete.mdx new file mode 100644 index 000000000000..15fdd6644126 --- /dev/null +++ b/api-reference-v2/api-reference/merchant-connector-account/merchant-connector--delete.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v2/connector-accounts/{id} +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/payment-methods/list-saved-payment-methods-for-a-customer.mdx b/api-reference-v2/api-reference/payment-methods/list-saved-payment-methods-for-a-customer.mdx new file mode 100644 index 000000000000..ef5a27f9604d --- /dev/null +++ b/api-reference-v2/api-reference/payment-methods/list-saved-payment-methods-for-a-customer.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/customers/{id}/saved-payment-methods +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/payments/payments--payment-methods-list.mdx b/api-reference-v2/api-reference/payments/payments--payment-methods-list.mdx new file mode 100644 index 000000000000..87e2a93586cc --- /dev/null +++ b/api-reference-v2/api-reference/payments/payments--payment-methods-list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/payments/{id}/payment-methods +--- \ No newline at end of file diff --git a/api-reference-v2/mint.json b/api-reference-v2/mint.json index 4212b2dbd213..080adae5ad1c 100644 --- a/api-reference-v2/mint.json +++ b/api-reference-v2/mint.json @@ -23,9 +23,7 @@ "navigation": [ { "group": "Get Started", - "pages": [ - "introduction" - ] + "pages": ["introduction"] }, { "group": "Essentials", @@ -42,6 +40,7 @@ "api-reference/payments/payments--get-intent", "api-reference/payments/payments--update-intent", "api-reference/payments/payments--session-token", + "api-reference/payments/payments--payment-methods-list", "api-reference/payments/payments--confirm-intent", "api-reference/payments/payments--get" ] @@ -135,10 +134,8 @@ "github": "https://github.com/juspay/hyperswitch", "linkedin": "https://www.linkedin.com/company/hyperswitch" }, - "openapi": [ - "openapi_spec.json" - ], + "openapi": ["openapi_spec.json"], "api": { "maintainOrder": true } -} \ No newline at end of file +} diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 20ec4157f566..3f9167ab7578 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -674,57 +674,6 @@ ] } }, - "/v2/payments/{payment_id}/create-external-sdk-tokens": { - "post": { - "tags": [ - "Payments" - ], - "summary": "Payments - Session token", - "description": "Creates a session object or a session token for wallets like Apple Pay, Google Pay, etc. These tokens are used by Hyperswitch's SDK to initiate these wallets' SDK.", - "operationId": "Create Session tokens for a Payment", - "parameters": [ - { - "name": "payment_id", - "in": "path", - "description": "The identifier for payment", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentsSessionRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Payment session object created or session token was retrieved from wallets", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentsSessionResponse" - } - } - } - }, - "400": { - "description": "Missing mandatory fields" - } - }, - "security": [ - { - "publishable_key": [] - } - ] - } - }, "/v2/profiles": { "post": { "tags": [ @@ -1992,6 +1941,41 @@ "summary": "Payments - Confirm Intent", "description": "**Confirms a payment intent object with the payment method data**\n\n.", "operationId": "Confirm Payment Intent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The unique identifier for the Payment Intent", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-Profile-Id", + "in": "header", + "description": "Profile ID associated to the payment intent", + "required": true, + "schema": { + "type": "string" + }, + "example": { + "X-Profile-Id": "pro_abcdefghijklmnop" + } + }, + { + "name": "X-Client-Secret", + "in": "header", + "description": "Client Secret Associated with the payment intent", + "required": true, + "schema": { + "type": "string" + }, + "example": { + "X-Client-Secret": "12345_pay_0193e41106e07e518940f8b51b9c8121_secret_0193e41107027a928d61d292e6a5dba9" + } + } + ], "requestBody": { "content": { "application/json": { @@ -2035,7 +2019,7 @@ }, "security": [ { - "publisable_key": [] + "publishable_key": [] } ] } @@ -2090,6 +2074,122 @@ ] } }, + "/v2/payments/{payment_id}/create-external-sdk-tokens": { + "post": { + "tags": [ + "Payments" + ], + "summary": "Payments - Session token", + "description": "Creates a session object or a session token for wallets like Apple Pay, Google Pay, etc. These tokens are used by Hyperswitch's SDK to initiate these wallets' SDK.", + "operationId": "Create Session tokens for a Payment", + "parameters": [ + { + "name": "payment_id", + "in": "path", + "description": "The identifier for payment", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentsSessionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Payment session object created or session token was retrieved from wallets", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentsSessionResponse" + } + } + } + }, + "400": { + "description": "Missing mandatory fields" + } + }, + "security": [ + { + "publishable_key": [] + } + ] + } + }, + "/v2/payments/{id}/payment-methods": { + "get": { + "tags": [ + "Payments" + ], + "summary": "Payments - Payment Methods List", + "description": "List the payment methods eligible for a payment. This endpoint also returns the saved payment methods for the customer when the customer_id is passed when creating the payment", + "operationId": "Retrieve Payment methods for a Payment", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The global payment id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-Profile-Id", + "in": "header", + "description": "Profile ID associated to the payment intent", + "required": true, + "schema": { + "type": "string" + }, + "example": { + "X-Profile-Id": "pro_abcdefghijklmnop" + } + }, + { + "name": "X-Client-Secret", + "in": "header", + "description": "Client Secret Associated with the payment intent", + "required": true, + "schema": { + "type": "string" + }, + "example": { + "X-Client-Secret": "12345_pay_0193e41106e07e518940f8b51b9c8121_secret_0193e41107027a928d61d292e6a5dba9" + } + } + ], + "responses": { + "200": { + "description": "Get the payment methods", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentMethodListResponseForPayments" + } + } + } + }, + "404": { + "description": "No payment found with the given id" + } + }, + "security": [ + { + "publishable_key": [] + } + ] + } + }, "/v2/payments/{id}/saved-payment-methods": { "get": { "tags": [ @@ -10599,7 +10699,8 @@ "required": [ "connector_type", "connector_name", - "profile_id" + "profile_id", + "payment_methods_enabled" ], "properties": { "connector_type": { @@ -10628,48 +10729,7 @@ "nullable": true }, "payment_methods_enabled": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PaymentMethodsEnabled" - }, - "description": "An object containing the details about the payment methods that need to be enabled under this merchant connector account", - "example": [ - { - "accepted_countries": { - "list": [ - "FR", - "DE", - "IN" - ], - "type": "disable_only" - }, - "accepted_currencies": { - "list": [ - "USD", - "EUR" - ], - "type": "enable_only" - }, - "installment_payment_enabled": true, - "maximum_amount": 68607706, - "minimum_amount": 1, - "payment_method": "wallet", - "payment_method_issuers": [ - "labore magna ipsum", - "aute" - ], - "payment_method_types": [ - "upi_collect", - "upi_intent" - ], - "payment_schemes": [ - "Discover", - "Discover" - ], - "recurring_enabled": true - } - ], - "nullable": true + "$ref": "#/components/schemas/PaymentMethodsEnabled" }, "connector_webhook_details": { "allOf": [ @@ -10812,6 +10872,7 @@ "connector_name", "id", "profile_id", + "payment_methods_enabled", "status" ], "properties": { @@ -10842,44 +10903,7 @@ "items": { "$ref": "#/components/schemas/PaymentMethodsEnabled" }, - "description": "An object containing the details about the payment methods that need to be enabled under this merchant connector account", - "example": [ - { - "accepted_countries": { - "list": [ - "FR", - "DE", - "IN" - ], - "type": "disable_only" - }, - "accepted_currencies": { - "list": [ - "USD", - "EUR" - ], - "type": "enable_only" - }, - "installment_payment_enabled": true, - "maximum_amount": 68607706, - "minimum_amount": 1, - "payment_method": "wallet", - "payment_method_issuers": [ - "labore magna ipsum", - "aute" - ], - "payment_method_types": [ - "upi_collect", - "upi_intent" - ], - "payment_schemes": [ - "Discover", - "Discover" - ], - "recurring_enabled": true - } - ], - "nullable": true + "description": "An object containing the details about the payment methods that need to be enabled under this merchant connector account" }, "disabled": { "type": "boolean", @@ -10923,6 +10947,7 @@ "connector_name", "id", "profile_id", + "payment_methods_enabled", "status" ], "properties": { @@ -10961,44 +10986,7 @@ "items": { "$ref": "#/components/schemas/PaymentMethodsEnabled" }, - "description": "An object containing the details about the payment methods that need to be enabled under this merchant connector account", - "example": [ - { - "accepted_countries": { - "list": [ - "FR", - "DE", - "IN" - ], - "type": "disable_only" - }, - "accepted_currencies": { - "list": [ - "USD", - "EUR" - ], - "type": "enable_only" - }, - "installment_payment_enabled": true, - "maximum_amount": 68607706, - "minimum_amount": 1, - "payment_method": "wallet", - "payment_method_issuers": [ - "labore magna ipsum", - "aute" - ], - "payment_method_types": [ - "upi_collect", - "upi_intent" - ], - "payment_schemes": [ - "Discover", - "Discover" - ], - "recurring_enabled": true - } - ], - "nullable": true + "description": "An object containing the details about the payment methods that need to be enabled under this merchant connector account" }, "connector_webhook_details": { "allOf": [ @@ -11096,42 +11084,6 @@ "$ref": "#/components/schemas/PaymentMethodsEnabled" }, "description": "An object containing the details about the payment methods that need to be enabled under this merchant connector account", - "example": [ - { - "accepted_countries": { - "list": [ - "FR", - "DE", - "IN" - ], - "type": "disable_only" - }, - "accepted_currencies": { - "list": [ - "USD", - "EUR" - ], - "type": "enable_only" - }, - "installment_payment_enabled": true, - "maximum_amount": 68607706, - "minimum_amount": 1, - "payment_method": "wallet", - "payment_method_issuers": [ - "labore magna ipsum", - "aute" - ], - "payment_method_types": [ - "upi_collect", - "upi_intent" - ], - "payment_schemes": [ - "Discover", - "Discover" - ], - "recurring_enabled": true - } - ], "nullable": true }, "connector_webhook_details": { @@ -14105,6 +14057,29 @@ } } }, + "PaymentMethodListResponseForPayments": { + "type": "object", + "required": [ + "payment_methods_enabled" + ], + "properties": { + "payment_methods_enabled": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ResponsePaymentMethodTypes" + }, + "description": "The list of payment methods that are enabled for the business profile" + }, + "customer_payment_methods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerPaymentMethod" + }, + "description": "The list of payment methods that are saved by the given customer\nThis field is only returned if the customer_id is provided in the request", + "nullable": true + } + } + }, "PaymentMethodResponse": { "type": "object", "required": [ @@ -14201,6 +14176,38 @@ "awaiting_data" ] }, + "PaymentMethodSubtypeSpecificData": { + "oneOf": [ + { + "type": "object", + "required": [ + "card_networks" + ], + "properties": { + "card_networks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CardNetworkTypes" + } + } + } + }, + { + "type": "object", + "required": [ + "bank_names" + ], + "properties": { + "bank_names": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BankCodeResponse" + } + } + } + } + ] + }, "PaymentMethodType": { "type": "string", "description": "Indicates the sub type of payment method. Eg: 'google_pay' & 'apple_pay' for wallets.", @@ -14333,21 +14340,18 @@ "type": "object", "description": "Details of all the payment methods enabled for the connector for the given merchant account", "required": [ - "payment_method" + "payment_method_type" ], "properties": { - "payment_method": { + "payment_method_type": { "$ref": "#/components/schemas/PaymentMethod" }, - "payment_method_types": { + "payment_method_subtypes": { "type": "array", "items": { "$ref": "#/components/schemas/RequestPaymentMethodTypes" }, - "description": "Subtype of payment method", - "example": [ - "credit" - ], + "description": "Payment method configuration, this includes all the filters associated with the payment method", "nullable": true } }, @@ -17964,8 +17968,7 @@ "ProfileCreate": { "type": "object", "required": [ - "profile_name", - "is_click_to_pay_enabled" + "profile_name" ], "properties": { "profile_name": { @@ -19092,76 +19095,47 @@ } }, "ResponsePaymentMethodTypes": { - "type": "object", - "required": [ - "payment_method_subtype" - ], - "properties": { - "payment_method_subtype": { - "$ref": "#/components/schemas/PaymentMethodType" - }, - "payment_experience": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PaymentExperienceTypes" - }, - "description": "The list of payment experiences enabled, if applicable for a payment method type", - "nullable": true - }, - "card_networks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CardNetworkTypes" - }, - "description": "The list of card networks enabled, if applicable for a payment method type", - "nullable": true - }, - "bank_names": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BankCodeResponse" - }, - "description": "The list of banks enabled, if applicable for a payment method type", - "nullable": true - }, - "bank_debits": { - "allOf": [ - { - "$ref": "#/components/schemas/BankDebitTypes" - } - ], - "nullable": true - }, - "bank_transfers": { + "allOf": [ + { "allOf": [ { - "$ref": "#/components/schemas/BankTransferTypes" + "$ref": "#/components/schemas/PaymentMethodSubtypeSpecificData" } ], "nullable": true }, - "required_fields": { + { "type": "object", - "description": "Required fields for the payment_method_type.", - "additionalProperties": { - "$ref": "#/components/schemas/RequiredFieldInfo" - }, - "nullable": true - }, - "surcharge_details": { - "allOf": [ - { - "$ref": "#/components/schemas/SurchargeDetailsResponse" - } + "required": [ + "payment_method_type", + "payment_method_subtype" ], - "nullable": true - }, - "pm_auth_connector": { - "type": "string", - "description": "auth service connector label for this payment method type, if exists", - "nullable": true + "properties": { + "payment_method_type": { + "$ref": "#/components/schemas/PaymentMethodType" + }, + "payment_method_subtype": { + "$ref": "#/components/schemas/PaymentMethodType" + }, + "required_fields": { + "type": "object", + "description": "Required fields for the payment_method_type.\nThis is the union of all the required fields for the payment method type enabled in all the connectors.", + "additionalProperties": { + "$ref": "#/components/schemas/RequiredFieldInfo" + }, + "nullable": true + }, + "surcharge_details": { + "allOf": [ + { + "$ref": "#/components/schemas/SurchargeDetailsResponse" + } + ], + "nullable": true + } + } } - } + ] }, "ResponsePaymentMethodsEnabled": { "type": "object", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 76db62bbeaff..ef25a4350d95 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -676,9 +676,11 @@ pub struct MerchantConnectorCreate { /// 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: api_enums::Connector, + /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports, If not passed then if will take `connector_name`_`profile_name`. Eg: if your profile label is `default`, connector label can be `stripe_default` #[schema(example = "stripe_US_travel")] pub connector_label: Option, @@ -692,36 +694,8 @@ pub struct MerchantConnectorCreate { pub connector_account_details: 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>, + #[schema(value_type = PaymentMethodsEnabled)] + pub payment_methods_enabled: Option>, /// Webhook details of this merchant connector #[schema(example = json!({ @@ -1056,36 +1030,8 @@ pub struct MerchantConnectorResponse { pub connector_account_details: pii::SecretSerdeValue, /// 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>, + #[schema(value_type = Vec)] + pub payment_methods_enabled: Option>, /// Webhook details of this merchant connector #[schema(example = json!({ @@ -1381,36 +1327,8 @@ pub struct MerchantConnectorListResponse { pub profile_id: id_type::ProfileId, /// 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>, + #[schema(value_type = Vec)] + pub payment_methods_enabled: Option>, /// A boolean value to indicate if the connector is disabled. By default, its value is false. #[schema(default = false, example = false)] @@ -1568,36 +1486,8 @@ pub struct MerchantConnectorUpdate { pub connector_account_details: 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>, + #[schema(value_type = Option>)] + pub payment_methods_enabled: Option>, /// Webhook details of this merchant connector #[schema(example = json!({ @@ -2085,6 +1975,7 @@ pub struct ProfileCreate { /// Indicates if click to pay is enabled or not. #[schema(default = false, example = false)] + #[serde(default)] pub is_click_to_pay_enabled: bool, /// Product authentication ids diff --git a/crates/api_models/src/events/payment.rs b/crates/api_models/src/events/payment.rs index a78aaf483aa0..c242788e090b 100644 --- a/crates/api_models/src/events/payment.rs +++ b/crates/api_models/src/events/payment.rs @@ -420,7 +420,15 @@ impl ApiEventMetric for PaymentStartRedirectionRequest { } #[cfg(feature = "v2")] -impl ApiEventMetric for events::PaymentsCaptureResponse { +impl ApiEventMetric for payments::PaymentMethodListResponseForPayments { + // Payment id would be populated by the request + fn get_api_event_type(&self) -> Option { + None + } +} + +#[cfg(feature = "v2")] +impl ApiEventMetric for payments::PaymentsCaptureResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Payment { payment_id: self.id.clone(), diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index fe5cb2933f9e..18d18f08fd24 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -1242,36 +1242,39 @@ pub struct ResponsePaymentMethodTypes { pub pm_auth_connector: Option, } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)] +#[serde(untagged)] // Untagged used for serialization only +pub enum PaymentMethodSubtypeSpecificData { + Card { + card_networks: Vec, + }, + Bank { + bank_names: Vec, + }, +} + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, Clone, serde::Serialize, ToSchema, PartialEq)] pub struct ResponsePaymentMethodTypes { /// The payment method type enabled #[schema(example = "klarna", value_type = PaymentMethodType)] - pub payment_method_subtype: api_enums::PaymentMethodType, - - /// The list of payment experiences enabled, if applicable for a payment method type - pub payment_experience: Option>, - - /// The list of card networks enabled, if applicable for a payment method type - pub card_networks: Option>, + pub payment_method_type: common_enums::PaymentMethod, - /// The list of banks enabled, if applicable for a payment method type - pub bank_names: Option>, - - /// The Bank debit payment method information, if applicable for a payment method type. - pub bank_debits: Option, + /// The payment method subtype enabled + #[schema(example = "klarna", value_type = PaymentMethodType)] + pub payment_method_subtype: common_enums::PaymentMethodType, - /// The Bank transfer payment method information, if applicable for a payment method type. - pub bank_transfers: Option, + /// payment method subtype specific information + #[serde(flatten)] + pub extra_information: Option, /// Required fields for the payment_method_type. + /// This is the union of all the required fields for the payment method type enabled in all the connectors. pub required_fields: Option>, /// surcharge details for this payment method type if exists pub surcharge_details: Option, - - /// auth service connector label for this payment method type, if exists - pub pm_auth_connector: Option, } #[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ed537c3efeea..a17189440954 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -27,6 +27,8 @@ use time::{Date, PrimitiveDateTime}; use url::Url; use utoipa::ToSchema; +#[cfg(feature = "v2")] +use crate::payment_methods; use crate::{ admin::{self, MerchantConnectorInfo}, disputes, enums as api_enums, @@ -6548,6 +6550,23 @@ pub struct SdkInformation { pub sdk_max_timeout: u8, } +#[cfg(feature = "v2")] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] +pub struct PaymentMethodsListRequest {} + +#[cfg(feature = "v2")] +#[derive(Debug, serde::Serialize, ToSchema)] +pub struct PaymentMethodListResponseForPayments { + /// The list of payment methods that are enabled for the business profile + #[schema(value_type = Vec)] + pub payment_methods_enabled: Vec, + + /// The list of payment methods that are saved by the given customer + /// This field is only returned if the customer_id is provided in the request + #[schema(value_type = Option>)] + pub customer_payment_methods: Option>, +} + #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] pub struct PaymentsExternalAuthenticationResponse { /// Indicates the transaction status diff --git a/crates/common_types/Cargo.toml b/crates/common_types/Cargo.toml index b21d7211a344..33dd799f0f60 100644 --- a/crates/common_types/Cargo.toml +++ b/crates/common_types/Cargo.toml @@ -6,6 +6,11 @@ edition.workspace = true rust-version.workspace = true license.workspace = true +[features] +default = [] +v1 = ["common_utils/v1"] +v2 = ["common_utils/v2"] + [dependencies] diesel = "2.2.3" serde = { version = "1.0.197", features = ["derive"] } diff --git a/crates/common_types/src/lib.rs b/crates/common_types/src/lib.rs index 30b46d98ae2f..b0b258ecb6d8 100644 --- a/crates/common_types/src/lib.rs +++ b/crates/common_types/src/lib.rs @@ -2,5 +2,6 @@ #![warn(missing_docs, missing_debug_implementations)] +pub mod payment_methods; pub mod payments; pub mod refunds; diff --git a/crates/common_types/src/payment_methods.rs b/crates/common_types/src/payment_methods.rs new file mode 100644 index 000000000000..702d8c0e7000 --- /dev/null +++ b/crates/common_types/src/payment_methods.rs @@ -0,0 +1,126 @@ +//! Common types to be used in payment methods + +use diesel::{ + backend::Backend, deserialize, deserialize::FromSql, sql_types::Jsonb, AsExpression, Queryable, +}; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +/// Details of all the payment methods enabled for the connector for the given merchant account +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, AsExpression)] +#[serde(deny_unknown_fields)] +#[diesel(sql_type = Jsonb)] +pub struct PaymentMethodsEnabled { + /// Type of payment method. + #[schema(value_type = PaymentMethod,example = "card")] + pub payment_method_type: common_enums::PaymentMethod, + + /// Payment method configuration, this includes all the filters associated with the payment method + pub payment_method_subtypes: Option>, +} + +/// Details of a specific payment method subtype enabled for the connector for the given merchant account +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq, Hash)] +pub struct RequestPaymentMethodTypes { + /// The payment method subtype + #[schema(value_type = PaymentMethodType)] + pub payment_method_subtype: common_enums::PaymentMethodType, + + /// The payment experience for the payment method + #[schema(value_type = Option)] + pub payment_experience: Option, + + /// List of cards networks that are enabled for this payment method, applicable for credit and debit payment method subtypes only + #[schema(value_type = Option>)] + pub card_networks: Option>, + /// List of currencies accepted or has the processing capabilities of the processor + #[schema(example = json!( + { + "type": "enable_only", + "list": ["USD", "INR"] + } + ), value_type = Option)] + pub accepted_currencies: Option, + + /// List of Countries accepted or has the processing capabilities of the processor + #[schema(example = json!( + { + "type": "enable_only", + "list": ["UK", "AU"] + } + ), value_type = Option)] + pub accepted_countries: Option, + + /// Minimum amount supported by the processor. To be represented in the lowest denomination of the target currency (For example, for USD it should be in cents) + #[schema(example = 1)] + pub minimum_amount: Option, + + /// Maximum amount supported by the processor. To be represented in the lowest denomination of + /// the target currency (For example, for USD it should be in cents) + #[schema(example = 1313)] + pub maximum_amount: Option, + + /// Boolean to enable recurring payments / mandates. Default is true. + #[schema(default = true, example = false)] + pub recurring_enabled: bool, + + /// Boolean to enable installment / EMI / BNPL payments. Default is true. + #[schema(default = true, example = false)] + pub installment_payment_enabled: bool, +} + +#[derive(PartialEq, Eq, Hash, Debug, Clone, serde::Serialize, Deserialize, ToSchema)] +#[serde( + deny_unknown_fields, + tag = "type", + content = "list", + rename_all = "snake_case" +)] +/// Object to filter the countries for which the payment method subtype is enabled +pub enum AcceptedCountries { + /// Only enable the payment method subtype for specific countries + #[schema(value_type = Vec)] + EnableOnly(Vec), + + /// Only disable the payment method subtype for specific countries + #[schema(value_type = Vec)] + DisableOnly(Vec), + + /// Enable the payment method subtype for all countries, in which the processor has the processing capabilities + AllAccepted, +} + +#[derive(PartialEq, Eq, Hash, Debug, Clone, serde::Serialize, Deserialize, ToSchema)] +#[serde( + deny_unknown_fields, + tag = "type", + content = "list", + rename_all = "snake_case" +)] +/// Object to filter the countries for which the payment method subtype is enabled +pub enum AcceptedCurrencies { + /// Only enable the payment method subtype for specific currencies + #[schema(value_type = Vec)] + EnableOnly(Vec), + + /// Only disable the payment method subtype for specific currencies + #[schema(value_type = Vec)] + DisableOnly(Vec), + + /// Enable the payment method subtype for all currencies, in which the processor has the processing capabilities + AllAccepted, +} + +impl Queryable for PaymentMethodsEnabled +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } +} + +common_utils::impl_to_sql_from_sql_json!(PaymentMethodsEnabled); diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index f78886b8c936..1ecff0fc70a0 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -76,8 +76,8 @@ pub struct MerchantConnectorAccount { pub connector_name: String, pub connector_account_details: Encryption, pub disabled: Option, - #[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, @@ -146,7 +146,8 @@ pub struct MerchantConnectorAccountNew { pub connector_name: Option, pub connector_account_details: Option, pub disabled: Option, - pub payment_methods_enabled: Option>, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub payment_methods_enabled: Option>, pub metadata: Option, pub connector_label: Option, pub created_at: time::PrimitiveDateTime, @@ -199,7 +200,8 @@ pub struct MerchantConnectorAccountUpdateInternal { pub connector_account_details: Option, pub connector_label: Option, pub disabled: Option, - pub payment_methods_enabled: Option>, + #[diesel(deserialize_as = super::OptionalDieselArray)] + pub payment_methods_enabled: Option>, pub metadata: Option, pub modified_at: Option, pub connector_webhook_details: Option, diff --git a/crates/diesel_models/src/query/merchant_connector_account.rs b/crates/diesel_models/src/query/merchant_connector_account.rs index 0f0954420047..7abc0d6efde7 100644 --- a/crates/diesel_models/src/query/merchant_connector_account.rs +++ b/crates/diesel_models/src/query/merchant_connector_account.rs @@ -241,4 +241,22 @@ impl MerchantConnectorAccount { ) .await } + + pub async fn list_enabled_by_profile_id( + conn: &PgPooledConn, + profile_id: &common_utils::id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> StorageResult> { + generics::generic_filter::<::Table, _, _, _>( + conn, + dsl::profile_id + .eq(profile_id.to_owned()) + .and(dsl::disabled.eq(false)) + .and(dsl::connector_type.eq(connector_type)), + None, + None, + Some(dsl::created_at.asc()), + ) + .await + } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index fcfe05e5731c..d85dc601490c 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -722,7 +722,7 @@ diesel::table! { connector_name -> Varchar, connector_account_details -> Bytea, disabled -> Nullable, - payment_methods_enabled -> Nullable>>, + payment_methods_enabled -> Nullable>>, connector_type -> ConnectorType, metadata -> Nullable, #[max_length = 255] diff --git a/crates/euclid_wasm/Cargo.toml b/crates/euclid_wasm/Cargo.toml index f8c97699d06d..5df76d08d5ea 100644 --- a/crates/euclid_wasm/Cargo.toml +++ b/crates/euclid_wasm/Cargo.toml @@ -17,7 +17,8 @@ production = ["connector_configs/production"] development = ["connector_configs/development"] sandbox = ["connector_configs/sandbox"] payouts = ["api_models/payouts", "euclid/payouts"] -v1 = ["api_models/v1"] +v1 = ["api_models/v1", "kgraph_utils/v1"] +v2 = [] [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs index 9f610f9e6a33..b299ced68b01 100644 --- a/crates/euclid_wasm/src/lib.rs +++ b/crates/euclid_wasm/src/lib.rs @@ -7,8 +7,8 @@ use std::{ }; use api_models::{ - admin as admin_api, conditional_configs::ConditionalConfigs, enums as api_model_enums, - routing::ConnectorSelection, surcharge_decision_configs::SurchargeDecisionConfigs, + conditional_configs::ConditionalConfigs, enums as api_model_enums, routing::ConnectorSelection, + surcharge_decision_configs::SurchargeDecisionConfigs, }; use common_enums::RoutableConnectors; use connector_configs::{ @@ -20,7 +20,7 @@ use currency_conversion::{ }; use euclid::{ backend::{inputs, interpreter::InterpreterBackend, EuclidBackend}, - dssa::{self, analyzer, graph::CgraphExt, state_machine, truth}, + dssa::{self, analyzer, graph::CgraphExt, state_machine}, frontend::{ ast, dir::{self, enums as dir_enums, EuclidDirFilter}, @@ -76,9 +76,11 @@ pub fn convert_forex_value(amount: i64, from_currency: JsValue, to_currency: JsV /// This function can be used by the frontend to provide the WASM with information about /// all the merchant's connector accounts. The input argument is a vector of all the merchant's /// connector accounts from the API. +#[cfg(feature = "v1")] #[wasm_bindgen(js_name = seedKnowledgeGraph)] pub fn seed_knowledge_graph(mcas: JsValue) -> JsResult { - let mcas: Vec = serde_wasm_bindgen::from_value(mcas)?; + let mcas: Vec = + serde_wasm_bindgen::from_value(mcas)?; let connectors: Vec = mcas .iter() .map(|mca| { @@ -95,9 +97,11 @@ pub fn seed_knowledge_graph(mcas: JsValue) -> JsResult { default_configs: Some(pm_filter), }; let mca_graph = kgraph_utils::mca::make_mca_graph(mcas, &config).err_to_js()?; - let analysis_graph = - hyperswitch_constraint_graph::ConstraintGraph::combine(&mca_graph, &truth::ANALYSIS_GRAPH) - .err_to_js()?; + let analysis_graph = hyperswitch_constraint_graph::ConstraintGraph::combine( + &mca_graph, + &dssa::truth::ANALYSIS_GRAPH, + ) + .err_to_js()?; SEED_DATA .set(SeedData { diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index baec0e59f8a4..7789c05e9902 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -86,7 +86,7 @@ pub struct MerchantConnectorAccount { #[encrypt] pub connector_account_details: Encryptable>, pub disabled: Option, - pub payment_methods_enabled: Option>, + pub payment_methods_enabled: Option>, pub connector_type: enums::ConnectorType, pub metadata: Option, pub frm_configs: Option>, @@ -115,23 +115,6 @@ impl MerchantConnectorAccount { self.metadata.clone() } - pub fn get_parsed_payment_methods_enabled( - &self, - ) -> Vec> { - self.payment_methods_enabled - .clone() - .unwrap_or_default() - .into_iter() - .map(|payment_methods_enabled| { - payment_methods_enabled - .parse_value::("payment_methods_enabled") - .change_context(ApiErrorResponse::InvalidDataValue { - field_name: "payment_methods_enabled", - }) - }) - .collect() - } - pub fn is_disabled(&self) -> bool { self.disabled.unwrap_or(false) } @@ -152,6 +135,68 @@ impl MerchantConnectorAccount { } } +#[cfg(feature = "v2")] +/// Holds the payment methods enabled for a connector along with the connector name +/// This struct is a flattened representation of the payment methods enabled for a connector +#[derive(Debug)] +pub struct PaymentMethodsEnabledForConnector { + pub payment_methods_enabled: common_types::payment_methods::RequestPaymentMethodTypes, + pub payment_method: common_enums::PaymentMethod, + pub connector: String, +} + +#[cfg(feature = "v2")] +/// Holds the payment methods enabled for a connector +pub struct FlattenedPaymentMethodsEnabled { + pub payment_methods_enabled: Vec, +} + +#[cfg(feature = "v2")] +impl FlattenedPaymentMethodsEnabled { + /// This functions flattens the payment methods enabled from the connector accounts + /// Retains the connector name and payment method in every flattened element + pub fn from_payment_connectors_list(payment_connectors: Vec) -> Self { + let payment_methods_enabled_flattened_with_connector = payment_connectors + .into_iter() + .map(|connector| { + ( + connector.payment_methods_enabled.unwrap_or_default(), + connector.connector_name, + ) + }) + .flat_map(|(payment_method_enabled, connector_name)| { + payment_method_enabled + .into_iter() + .flat_map(move |payment_method| { + let request_payment_methods_enabled = + payment_method.payment_method_subtypes.unwrap_or_default(); + let length = request_payment_methods_enabled.len(); + request_payment_methods_enabled.into_iter().zip( + std::iter::repeat(( + connector_name.clone(), + payment_method.payment_method_type, + )) + .take(length), + ) + }) + }) + .map( + |(request_payment_methods, (connector_name, payment_method))| { + PaymentMethodsEnabledForConnector { + payment_methods_enabled: request_payment_methods, + connector: connector_name.clone(), + payment_method, + } + }, + ) + .collect(); + + Self { + payment_methods_enabled: payment_methods_enabled_flattened_with_connector, + } + } +} + #[cfg(feature = "v1")] #[derive(Debug)] pub enum MerchantConnectorAccountUpdate { @@ -185,7 +230,7 @@ pub enum MerchantConnectorAccountUpdate { connector_type: Option, connector_account_details: Box>>, disabled: Option, - payment_methods_enabled: Option>, + payment_methods_enabled: Option>, metadata: Option, frm_configs: Option>, connector_webhook_details: Option, @@ -583,34 +628,20 @@ common_utils::create_list_wrapper!( pub fn get_connector_and_supporting_payment_method_type_for_session_call( &self, ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { + // This vector is created to work around lifetimes + let ref_vector = Vec::default(); + let connector_and_supporting_payment_method_type = self.iter().flat_map(|connector_account| { connector_account - .get_parsed_payment_methods_enabled() - // TODO: make payment_methods_enabled strict type in DB - .into_iter() - .filter_map(|parsed_payment_method_result| { - parsed_payment_method_result - .inspect_err(|err| { - logger::error!(session_token_parsing_error=?err); - }) - .ok() + .payment_methods_enabled.as_ref() + .unwrap_or(&Vec::default()) + .iter() + .flat_map(|payment_method_types| payment_method_types.payment_method_subtypes.as_ref().unwrap_or(&ref_vector)) + .filter(|payment_method_types_enabled| { + payment_method_types_enabled.payment_experience == Some(api_models::enums::PaymentExperience::InvokeSdkClient) }) - .flat_map(|parsed_payment_methods_enabled| { - parsed_payment_methods_enabled - .payment_method_types - .unwrap_or_default() - .into_iter() - .filter(|payment_method_type| { - let is_invoke_sdk_client = matches!( - payment_method_type.payment_experience, - Some(api_models::enums::PaymentExperience::InvokeSdkClient) - ); - is_invoke_sdk_client - }) - .map(|payment_method_type| { - (connector_account, payment_method_type.payment_method_type) - }) - .collect::>() + .map(|payment_method_types| { + (connector_account, payment_method_types.payment_method_subtype) }) .collect::>() }).collect(); diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index 858c013f41dd..e492ec46eb5e 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -16,6 +16,7 @@ use euclid::{ use hyperswitch_constraint_graph::{CycleCheck, Memoization}; use kgraph_utils::{error::KgraphError, transformers::IntoDirValue, types::CountryCurrencyFilter}; +#[cfg(feature = "v1")] fn build_test_data( total_enabled: usize, total_pm_types: usize, @@ -54,25 +55,25 @@ fn build_test_data( let profile_id = common_utils::generate_profile_id_of_default_length(); - #[cfg(feature = "v2")] - let stripe_account = MerchantConnectorResponse { - connector_type: api_enums::ConnectorType::FizOperations, - connector_name: "stripe".to_string(), - id: common_utils::generate_merchant_connector_account_id_of_default_length(), - connector_account_details: masking::Secret::new(serde_json::json!({})), - disabled: None, - metadata: None, - payment_methods_enabled: Some(pms_enabled), - connector_label: Some("something".to_string()), - frm_configs: None, - connector_webhook_details: None, - profile_id, - applepay_verified_domains: None, - pm_auth_config: None, - status: api_enums::ConnectorStatus::Inactive, - additional_merchant_data: None, - connector_wallets_details: None, - }; + // #[cfg(feature = "v2")] + // let stripe_account = MerchantConnectorResponse { + // connector_type: api_enums::ConnectorType::FizOperations, + // connector_name: "stripe".to_string(), + // id: common_utils::generate_merchant_connector_account_id_of_default_length(), + // connector_account_details: masking::Secret::new(serde_json::json!({})), + // disabled: None, + // metadata: None, + // payment_methods_enabled: Some(pms_enabled), + // connector_label: Some("something".to_string()), + // frm_configs: None, + // connector_webhook_details: None, + // profile_id, + // applepay_verified_domains: None, + // pm_auth_config: None, + // status: api_enums::ConnectorStatus::Inactive, + // additional_merchant_data: None, + // connector_wallets_details: None, + // }; #[cfg(feature = "v1")] let stripe_account = MerchantConnectorResponse { @@ -102,10 +103,13 @@ fn build_test_data( connector_configs: HashMap::new(), default_configs: None, }; + + #[cfg(feature = "v1")] kgraph_utils::mca::make_mca_graph(vec![stripe_account], &config) .expect("Failed graph construction") } +#[cfg(feature = "v1")] fn evaluation(c: &mut Criterion) { let small_graph = build_test_data(3, 8); let big_graph = build_test_data(20, 20); @@ -149,5 +153,10 @@ fn evaluation(c: &mut Criterion) { }); } +#[cfg(feature = "v1")] criterion_group!(benches, evaluation); +#[cfg(feature = "v1")] criterion_main!(benches); + +#[cfg(feature = "v2")] +fn main() {} diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index c65ec864d321..a3e1dfc6220f 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -16,6 +16,7 @@ use crate::{error::KgraphError, transformers::IntoDirValue, types as kgraph_type pub const DOMAIN_IDENTIFIER: &str = "payment_methods_enabled_for_merchantconnectoraccount"; +#[cfg(feature = "v1")] fn get_dir_value_payment_method( from: api_enums::PaymentMethodType, ) -> Result { @@ -153,6 +154,7 @@ fn get_dir_value_payment_method( } } +#[cfg(feature = "v1")] fn compile_request_pm_types( builder: &mut cgraph::ConstraintGraphBuilder, pm_types: RequestPaymentMethodTypes, @@ -338,6 +340,7 @@ fn compile_request_pm_types( .map_err(KgraphError::GraphConstructionError) } +#[cfg(feature = "v1")] fn compile_payment_method_enabled( builder: &mut cgraph::ConstraintGraphBuilder, enabled: admin_api::PaymentMethodsEnabled, @@ -405,6 +408,8 @@ macro_rules! collect_global_variants { .collect::>() }; } + +#[cfg(feature = "v1")] fn global_vec_pmt( enabled_pmt: Vec, builder: &mut cgraph::ConstraintGraphBuilder, @@ -512,6 +517,7 @@ fn compile_graph_for_countries_and_currencies( .map_err(KgraphError::GraphConstructionError) } +#[cfg(feature = "v1")] fn compile_config_graph( builder: &mut cgraph::ConstraintGraphBuilder, config: &kgraph_types::CountryCurrencyFilter, @@ -605,6 +611,7 @@ fn compile_config_graph( .map_err(KgraphError::GraphConstructionError) } +#[cfg(feature = "v1")] fn compile_merchant_connector_graph( builder: &mut cgraph::ConstraintGraphBuilder, mca: admin_api::MerchantConnectorResponse, @@ -675,6 +682,7 @@ fn compile_merchant_connector_graph( Ok(()) } +#[cfg(feature = "v1")] pub fn make_mca_graph( accts: Vec, config: &kgraph_types::CountryCurrencyFilter, @@ -691,6 +699,7 @@ pub fn make_mca_graph( Ok(builder.build()) } +#[cfg(feature = "v1")] #[cfg(test)] mod tests { #![allow(clippy::expect_used)] @@ -711,61 +720,61 @@ mod tests { use api_models::{admin::*, payment_methods::*}; let profile_id = common_utils::generate_profile_id_of_default_length(); - #[cfg(feature = "v2")] - let stripe_account = MerchantConnectorResponse { - connector_type: api_enums::ConnectorType::FizOperations, - connector_name: "stripe".to_string(), - id: common_utils::generate_merchant_connector_account_id_of_default_length(), - connector_label: Some("something".to_string()), - connector_account_details: masking::Secret::new(serde_json::json!({})), - disabled: None, - metadata: None, - payment_methods_enabled: Some(vec![PaymentMethodsEnabled { - payment_method: api_enums::PaymentMethod::Card, - payment_method_types: Some(vec![ - RequestPaymentMethodTypes { - payment_method_type: api_enums::PaymentMethodType::Credit, - payment_experience: None, - card_networks: Some(vec![ - api_enums::CardNetwork::Visa, - api_enums::CardNetwork::Mastercard, - ]), - accepted_currencies: Some(AcceptedCurrencies::EnableOnly(vec![ - api_enums::Currency::INR, - ])), - accepted_countries: None, - minimum_amount: Some(MinorUnit::new(10)), - maximum_amount: Some(MinorUnit::new(1000)), - recurring_enabled: true, - installment_payment_enabled: true, - }, - RequestPaymentMethodTypes { - payment_method_type: api_enums::PaymentMethodType::Debit, - payment_experience: None, - card_networks: Some(vec![ - api_enums::CardNetwork::Maestro, - api_enums::CardNetwork::JCB, - ]), - accepted_currencies: Some(AcceptedCurrencies::EnableOnly(vec![ - api_enums::Currency::GBP, - ])), - accepted_countries: None, - minimum_amount: Some(MinorUnit::new(10)), - maximum_amount: Some(MinorUnit::new(1000)), - recurring_enabled: true, - installment_payment_enabled: true, - }, - ]), - }]), - frm_configs: None, - connector_webhook_details: None, - profile_id, - applepay_verified_domains: None, - pm_auth_config: None, - status: api_enums::ConnectorStatus::Inactive, - additional_merchant_data: None, - connector_wallets_details: None, - }; + // #[cfg(feature = "v2")] + // let stripe_account = MerchantConnectorResponse { + // connector_type: api_enums::ConnectorType::FizOperations, + // connector_name: "stripe".to_string(), + // id: common_utils::generate_merchant_connector_account_id_of_default_length(), + // connector_label: Some("something".to_string()), + // connector_account_details: masking::Secret::new(serde_json::json!({})), + // disabled: None, + // metadata: None, + // payment_methods_enabled: Some(vec![PaymentMethodsEnabled { + // payment_method: api_enums::PaymentMethod::Card, + // payment_method_types: Some(vec![ + // RequestPaymentMethodTypes { + // payment_method_type: api_enums::PaymentMethodType::Credit, + // payment_experience: None, + // card_networks: Some(vec![ + // api_enums::CardNetwork::Visa, + // api_enums::CardNetwork::Mastercard, + // ]), + // accepted_currencies: Some(AcceptedCurrencies::EnableOnly(vec![ + // api_enums::Currency::INR, + // ])), + // accepted_countries: None, + // minimum_amount: Some(MinorUnit::new(10)), + // maximum_amount: Some(MinorUnit::new(1000)), + // recurring_enabled: true, + // installment_payment_enabled: true, + // }, + // RequestPaymentMethodTypes { + // payment_method_type: api_enums::PaymentMethodType::Debit, + // payment_experience: None, + // card_networks: Some(vec![ + // api_enums::CardNetwork::Maestro, + // api_enums::CardNetwork::JCB, + // ]), + // accepted_currencies: Some(AcceptedCurrencies::EnableOnly(vec![ + // api_enums::Currency::GBP, + // ])), + // accepted_countries: None, + // minimum_amount: Some(MinorUnit::new(10)), + // maximum_amount: Some(MinorUnit::new(1000)), + // recurring_enabled: true, + // installment_payment_enabled: true, + // }, + // ]), + // }]), + // frm_configs: None, + // connector_webhook_details: None, + // profile_id, + // applepay_verified_domains: None, + // pm_auth_config: None, + // status: api_enums::ConnectorStatus::Inactive, + // additional_merchant_data: None, + // connector_wallets_details: None, + // }; #[cfg(feature = "v1")] let stripe_account = MerchantConnectorResponse { connector_type: api_enums::ConnectorType::FizOperations, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 7553a780c272..6ea55ba6b79b 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -87,9 +87,6 @@ Never share your secret api keys. Keep them guarded and secure. routes::merchant_account::merchant_account_update, routes::merchant_account::profiles_list, - // Routes for payments - routes::payments::payments_connector_session, - // Routes for profile routes::profile::profile_create, routes::profile::profile_retrieve, @@ -127,6 +124,8 @@ Never share your secret api keys. Keep them guarded and secure. routes::payments::payments_update_intent, routes::payments::payments_confirm_intent, routes::payments::payment_status, + routes::payments::payments_connector_session, + routes::payments::list_payment_methods, //Routes for payment methods routes::payment_method::list_customer_payment_method_for_payment, @@ -162,6 +161,7 @@ Never share your secret api keys. Keep them guarded and secure. common_types::payments::StripeSplitPaymentRequest, common_types::refunds::StripeSplitRefundRequest, common_utils::types::ChargeRefunds, + common_types::payment_methods::PaymentMethodsEnabled, common_types::refunds::SplitRefund, api_models::payments::SplitPaymentsResponse, api_models::payments::StripeSplitPaymentsResponse, @@ -198,6 +198,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payment_methods::PaymentMethodListRequest, api_models::payment_methods::PaymentMethodListResponse, api_models::payment_methods::ResponsePaymentMethodsEnabled, + api_models::payment_methods::PaymentMethodSubtypeSpecificData, api_models::payment_methods::ResponsePaymentMethodTypes, api_models::payment_methods::PaymentExperienceTypes, api_models::payment_methods::CardNetworkTypes, @@ -272,7 +273,6 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::FrmConfigs, api_models::admin::FrmPaymentMethod, api_models::admin::FrmPaymentMethodType, - api_models::admin::PaymentMethodsEnabled, api_models::admin::MerchantConnectorDetailsWrap, api_models::admin::MerchantConnectorDetails, api_models::admin::MerchantConnectorWebhookDetails, @@ -474,6 +474,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentsConfirmIntentResponse, api_models::payments::AmountDetailsResponse, api_models::payments::BankCodeResponse, + api_models::payments::PaymentMethodListResponseForPayments, api_models::payment_methods::RequiredFieldInfo, api_models::payment_methods::DefaultPaymentMethod, api_models::payment_methods::MaskedBankDetails, diff --git a/crates/openapi/src/routes/payments.rs b/crates/openapi/src/routes/payments.rs index 927f4e69e3a8..33bb95618e89 100644 --- a/crates/openapi/src/routes/payments.rs +++ b/crates/openapi/src/routes/payments.rs @@ -691,6 +691,18 @@ pub fn payments_update_intent() {} #[utoipa::path( post, path = "/v2/payments/{id}/confirm-intent", + params (("id" = String, Path, description = "The unique identifier for the Payment Intent"), + ( + "X-Profile-Id" = String, Header, + description = "Profile ID associated to the payment intent", + example = json!({"X-Profile-Id": "pro_abcdefghijklmnop"}) + ), + ( + "X-Client-Secret" = String, Header, + description = "Client Secret Associated with the payment intent", + example = json!({"X-Client-Secret": "12345_pay_0193e41106e07e518940f8b51b9c8121_secret_0193e41107027a928d61d292e6a5dba9"}) + ), + ), request_body( content = PaymentsConfirmIntentRequest, examples( @@ -718,7 +730,7 @@ pub fn payments_update_intent() {} ), tag = "Payments", operation_id = "Confirm Payment Intent", - security(("publisable_key" = [])), + security(("publishable_key" = [])), )] #[cfg(feature = "v2")] pub fn payments_confirm_intent() {} @@ -752,3 +764,33 @@ pub(crate) enum ForceSync { /// Do not force sync with the connector / processor. Get the status which is available in the database False, } + +/// Payments - Payment Methods List +/// +/// List the payment methods eligible for a payment. This endpoint also returns the saved payment methods for the customer when the customer_id is passed when creating the payment +#[cfg(feature = "v2")] +#[utoipa::path( + get, + path = "/v2/payments/{id}/payment-methods", + params( + ("id" = String, Path, description = "The global payment id"), + ( + "X-Profile-Id" = String, Header, + description = "Profile ID associated to the payment intent", + example = json!({"X-Profile-Id": "pro_abcdefghijklmnop"}) + ), + ( + "X-Client-Secret" = String, Header, + description = "Client Secret Associated with the payment intent", + example = json!({"X-Client-Secret": "12345_pay_0193e41106e07e518940f8b51b9c8121_secret_0193e41107027a928d61d292e6a5dba9"}) + ), + ), + responses( + (status = 200, description = "Get the payment methods", body = PaymentMethodListResponseForPayments), + (status = 404, description = "No payment found with the given id") + ), + tag = "Payments", + operation_id = "Retrieve Payment methods for a Payment", + security(("publishable_key" = [])) +)] +pub fn list_payment_methods() {} diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index ccb563b06102..9cfa3f40b5f1 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1702,39 +1702,14 @@ impl ConnectorStatusAndDisabledValidation<'_> { } (Some(disabled), _) => Some(*disabled), (None, common_enums::ConnectorStatus::Inactive) => Some(true), - (None, _) => None, + // Enable the connector if nothing is passed in the request + (None, _) => Some(false), }; Ok((*connector_status, disabled)) } } -struct PaymentMethodsEnabled<'a> { - payment_methods_enabled: &'a Option>, -} - -impl PaymentMethodsEnabled<'_> { - fn get_payment_methods_enabled(&self) -> RouterResult>> { - let mut vec = Vec::new(); - let payment_methods_enabled = match self.payment_methods_enabled.clone() { - Some(val) => { - for pm in val.into_iter() { - let pm_value = pm - .encode_to_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Failed while encoding to serde_json::Value, PaymentMethod", - )?; - vec.push(Secret::new(pm_value)) - } - Some(vec) - } - None => None, - }; - Ok(payment_methods_enabled) - } -} - struct ConnectorMetadata<'a> { connector_metadata: &'a Option, } @@ -2118,13 +2093,10 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect key_manager_state: &KeyManagerState, merchant_account: &domain::MerchantAccount, ) -> RouterResult { - let payment_methods_enabled = PaymentMethodsEnabled { - payment_methods_enabled: &self.payment_methods_enabled, - }; - let payment_methods_enabled = payment_methods_enabled.get_payment_methods_enabled()?; - let frm_configs = self.get_frm_config_as_secret(); + let payment_methods_enabled = self.payment_methods_enabled; + let auth = types::ConnectorAuthType::from_secret_value( self.connector_account_details .clone() @@ -2458,11 +2430,8 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { ) -> RouterResult { // If connector label is not passed in the request, generate one let connector_label = self.get_connector_label(business_profile.profile_name.clone()); - let payment_methods_enabled = PaymentMethodsEnabled { - payment_methods_enabled: &self.payment_methods_enabled, - }; - let payment_methods_enabled = payment_methods_enabled.get_payment_methods_enabled()?; let frm_configs = self.get_frm_config_as_secret(); + let payment_methods_enabled = self.payment_methods_enabled; // Validate Merchant api details and return error if not in correct format let auth = types::ConnectorAuthType::from_option_secret_value( self.connector_account_details.clone(), @@ -2486,6 +2455,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { }; let (connector_status, disabled) = connector_status_and_disabled_validation.validate_status_and_disabled()?; + let identifier = km_types::Identifier::Merchant(business_profile.merchant_id.clone()); let merchant_recipient_data = if let Some(data) = &self.additional_merchant_data { Some( @@ -2606,6 +2576,34 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { } } +#[cfg(feature = "v1")] +struct PaymentMethodsEnabled<'a> { + payment_methods_enabled: &'a Option>, +} + +#[cfg(feature = "v1")] +impl PaymentMethodsEnabled<'_> { + fn get_payment_methods_enabled(&self) -> RouterResult>> { + let mut vec = Vec::new(); + let payment_methods_enabled = match self.payment_methods_enabled.clone() { + Some(val) => { + for pm in val.into_iter() { + let pm_value = pm + .encode_to_value() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Failed while encoding to serde_json::Value, PaymentMethod", + )?; + vec.push(Secret::new(pm_value)) + } + Some(vec) + } + None => None, + }; + Ok(payment_methods_enabled) + } +} + #[cfg(all(feature = "v1", feature = "olap"))] #[async_trait::async_trait] impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index e5e9ff92657a..164c0e9557af 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -3194,19 +3194,6 @@ fn get_val(str: String, val: &serde_json::Value) -> Option { .and_then(|v| v.as_str()) .map(|s| s.to_string()) } -#[cfg(all( - feature = "v2", - feature = "customer_v2", - feature = "payment_methods_v2" -))] -pub async fn list_payment_methods( - _state: routes::SessionState, - _merchant_account: domain::MerchantAccount, - _key_store: domain::MerchantKeyStore, - mut _req: api::PaymentMethodListRequest, -) -> errors::RouterResponse { - todo!() -} #[cfg(all( any(feature = "v1", feature = "v2"), diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index e68bd09ba462..452e672547b3 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -19,6 +19,9 @@ use std::{ collections::HashSet, fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoIter, }; +#[cfg(feature = "v2")] +pub mod payment_methods; + #[cfg(feature = "olap")] use api_models::admin::MerchantConnectorInfo; use api_models::{ diff --git a/crates/router/src/core/payments/payment_methods.rs b/crates/router/src/core/payments/payment_methods.rs new file mode 100644 index 000000000000..6ed436ae3c77 --- /dev/null +++ b/crates/router/src/core/payments/payment_methods.rs @@ -0,0 +1,214 @@ +//! Contains functions of payment methods that are used in payments +//! one of such functions is `list_payment_methods` + +use common_utils::{ext_traits::OptionExt, id_type}; +use error_stack::ResultExt; + +use super::errors; +use crate::{db::errors::StorageErrorExt, routes, types::domain}; + +#[cfg(all( + feature = "v2", + feature = "customer_v2", + feature = "payment_methods_v2" +))] +pub async fn list_payment_methods( + state: routes::SessionState, + merchant_account: domain::MerchantAccount, + profile: domain::Profile, + key_store: domain::MerchantKeyStore, + payment_id: id_type::GlobalPaymentId, + _req: api_models::payments::PaymentMethodsListRequest, + header_payload: &hyperswitch_domain_models::payments::HeaderPayload, +) -> errors::RouterResponse { + let db = &*state.store; + let key_manager_state = &(&state).into(); + + let payment_intent = db + .find_payment_intent_by_id( + key_manager_state, + &payment_id, + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + + validate_payment_status(payment_intent.status)?; + + let client_secret = header_payload + .client_secret + .as_ref() + .get_required_value("client_secret header") + .change_context(errors::ApiErrorResponse::MissingRequiredField { + field_name: "client_secret header", + })?; + + payment_intent.validate_client_secret(client_secret)?; + + let payment_connector_accounts = db + .list_enabled_connector_accounts_by_profile_id( + key_manager_state, + profile.get_id(), + &key_store, + common_enums::ConnectorType::PaymentProcessor, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("error when fetching merchant connector accounts")?; + + let response = + hyperswitch_domain_models::merchant_connector_account::FlattenedPaymentMethodsEnabled::from_payment_connectors_list(payment_connector_accounts) + .perform_filtering() + .get_required_fields(RequiredFieldsInput::new()) + .perform_surcharge_calculation() + .generate_response(); + + Ok(hyperswitch_domain_models::api::ApplicationResponse::Json( + response, + )) +} + +/// Container for the inputs required for the required fields +struct RequiredFieldsInput {} + +impl RequiredFieldsInput { + fn new() -> Self { + Self {} + } +} + +/// Container for the filtered payment methods +struct FilteredPaymentMethodsEnabled( + Vec, +); + +impl FilteredPaymentMethodsEnabled { + fn get_required_fields( + self, + _input: RequiredFieldsInput, + ) -> RequiredFieldsForEnabledPaymentMethodTypes { + let required_fields_info = self + .0 + .into_iter() + .map( + |payment_methods_enabled| RequiredFieldsForEnabledPaymentMethod { + required_field: None, + payment_method_type: payment_methods_enabled.payment_method, + payment_method_subtype: payment_methods_enabled + .payment_methods_enabled + .payment_method_subtype, + }, + ) + .collect(); + + RequiredFieldsForEnabledPaymentMethodTypes(required_fields_info) + } +} + +/// Element container to hold the filtered payment methods with required fields +struct RequiredFieldsForEnabledPaymentMethod { + required_field: + Option>, + payment_method_subtype: common_enums::PaymentMethodType, + payment_method_type: common_enums::PaymentMethod, +} + +/// Container to hold the filtered payment methods enabled with required fields +struct RequiredFieldsForEnabledPaymentMethodTypes(Vec); + +/// Element Container to hold the filtered payment methods enabled with required fields and surcharge +struct RequiredFieldsAndSurchargeForEnabledPaymentMethodType { + required_field: + Option>, + payment_method_subtype: common_enums::PaymentMethodType, + payment_method_type: common_enums::PaymentMethod, + surcharge: Option, +} + +/// Container to hold the filtered payment methods enabled with required fields and surcharge +struct RequiredFieldsAndSurchargeForEnabledPaymentMethodTypes( + Vec, +); + +impl RequiredFieldsAndSurchargeForEnabledPaymentMethodTypes { + fn generate_response(self) -> api_models::payments::PaymentMethodListResponseForPayments { + let response_payment_methods = self + .0 + .into_iter() + .map(|payment_methods_enabled| { + api_models::payment_methods::ResponsePaymentMethodTypes { + payment_method_type: payment_methods_enabled.payment_method_type, + payment_method_subtype: payment_methods_enabled.payment_method_subtype, + required_fields: payment_methods_enabled.required_field, + surcharge_details: payment_methods_enabled.surcharge, + extra_information: None, + } + }) + .collect(); + + api_models::payments::PaymentMethodListResponseForPayments { + payment_methods_enabled: response_payment_methods, + customer_payment_methods: None, + } + } +} + +impl RequiredFieldsForEnabledPaymentMethodTypes { + fn perform_surcharge_calculation( + self, + ) -> RequiredFieldsAndSurchargeForEnabledPaymentMethodTypes { + let details_with_surcharge = self + .0 + .into_iter() + .map( + |payment_methods_enabled| RequiredFieldsAndSurchargeForEnabledPaymentMethodType { + payment_method_type: payment_methods_enabled.payment_method_type, + required_field: payment_methods_enabled.required_field, + payment_method_subtype: payment_methods_enabled.payment_method_subtype, + surcharge: None, + }, + ) + .collect(); + + RequiredFieldsAndSurchargeForEnabledPaymentMethodTypes(details_with_surcharge) + } +} + +trait PerformFilteringOnPaymentMethodsEnabled { + fn perform_filtering(self) -> FilteredPaymentMethodsEnabled; +} + +impl PerformFilteringOnPaymentMethodsEnabled + for hyperswitch_domain_models::merchant_connector_account::FlattenedPaymentMethodsEnabled +{ + fn perform_filtering(self) -> FilteredPaymentMethodsEnabled { + FilteredPaymentMethodsEnabled(self.payment_methods_enabled) + } +} + +/// Validate if payment methods list can be performed on the current status of payment intent +fn validate_payment_status( + intent_status: common_enums::IntentStatus, +) -> Result<(), errors::ApiErrorResponse> { + match intent_status { + common_enums::IntentStatus::RequiresPaymentMethod => Ok(()), + common_enums::IntentStatus::Succeeded + | common_enums::IntentStatus::Failed + | common_enums::IntentStatus::Cancelled + | common_enums::IntentStatus::Processing + | common_enums::IntentStatus::RequiresCustomerAction + | common_enums::IntentStatus::RequiresMerchantAction + | common_enums::IntentStatus::RequiresCapture + | common_enums::IntentStatus::PartiallyCaptured + | common_enums::IntentStatus::RequiresConfirmation + | common_enums::IntentStatus::PartiallyCapturedAndCapturable => { + Err(errors::ApiErrorResponse::PaymentUnexpectedState { + current_flow: "list_payment_methods".to_string(), + field_name: "status".to_string(), + current_value: intent_status.to_string(), + states: ["requires_payment_method".to_string()].join(", "), + }) + } + } +} diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 6d3c0973283c..0a57819816fb 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -553,6 +553,7 @@ pub fn perform_volume_split( Ok(splits.into_iter().map(|sp| sp.connector).collect()) } +#[cfg(feature = "v1")] pub async fn get_merchant_cgraph<'a>( state: &SessionState, key_store: &domain::MerchantKeyStore, @@ -599,6 +600,7 @@ pub async fn get_merchant_cgraph<'a>( Ok(cgraph) } +#[cfg(feature = "v1")] pub async fn refresh_cgraph_cache<'a>( state: &SessionState, key_store: &domain::MerchantKeyStore, @@ -695,6 +697,21 @@ pub async fn refresh_cgraph_cache<'a>( Ok(cgraph) } +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments)] +pub async fn perform_cgraph_filtering( + state: &SessionState, + key_store: &domain::MerchantKeyStore, + chosen: Vec, + backend_input: dsl_inputs::BackendInput, + eligible_connectors: Option<&Vec>, + profile_id: &common_utils::id_type::ProfileId, + transaction_type: &api_enums::TransactionType, +) -> RoutingResult> { + todo!() +} + +#[cfg(feature = "v1")] #[allow(clippy::too_many_arguments)] pub async fn perform_cgraph_filtering( state: &SessionState, @@ -1124,80 +1141,78 @@ async fn perform_session_routing_for_pm_type( } } -#[cfg(feature = "v2")] -async fn perform_session_routing_for_pm_type( - session_pm_input: &SessionRoutingPmTypeInput<'_>, - transaction_type: &api_enums::TransactionType, - business_profile: &domain::Profile, -) -> RoutingResult>> { - let merchant_id = &session_pm_input.key_store.merchant_id; - - let MerchantAccountRoutingAlgorithm::V1(algorithm_id) = session_pm_input.routing_algorithm; - - let profile_wrapper = admin::ProfileWrapper::new(business_profile.clone()); - let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id { - let cached_algorithm = ensure_algorithm_cached_v1( - &session_pm_input.state.clone(), - merchant_id, - algorithm_id, - session_pm_input.profile_id, - transaction_type, - ) - .await?; - - match cached_algorithm.as_ref() { - CachedAlgorithm::Single(conn) => vec![(**conn).clone()], - CachedAlgorithm::Priority(plist) => plist.clone(), - CachedAlgorithm::VolumeSplit(splits) => { - perform_volume_split(splits.to_vec(), Some(session_pm_input.attempt_id)) - .change_context(errors::RoutingError::ConnectorSelectionFailed)? - } - CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1( - session_pm_input.backend_input.clone(), - interpreter, - )?, - } - } else { - profile_wrapper - .get_default_fallback_list_of_connector_under_profile() - .change_context(errors::RoutingError::FallbackConfigFetchFailed)? - }; - - let mut final_selection = perform_cgraph_filtering( - &session_pm_input.state.clone(), - session_pm_input.key_store, - chosen_connectors, - session_pm_input.backend_input.clone(), - None, - session_pm_input.profile_id, - transaction_type, - ) - .await?; - - if final_selection.is_empty() { - let fallback = profile_wrapper - .get_default_fallback_list_of_connector_under_profile() - .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; - - final_selection = perform_cgraph_filtering( - &session_pm_input.state.clone(), - session_pm_input.key_store, - fallback, - session_pm_input.backend_input.clone(), - None, - session_pm_input.profile_id, - transaction_type, - ) - .await?; - } - - if final_selection.is_empty() { - Ok(None) - } else { - Ok(Some(final_selection)) - } -} - +// async fn perform_session_routing_for_pm_type( +// session_pm_input: &SessionRoutingPmTypeInput<'_>, +// transaction_type: &api_enums::TransactionType, +// business_profile: &domain::Profile, +// ) -> RoutingResult>> { +// let merchant_id = &session_pm_input.key_store.merchant_id; + +// let MerchantAccountRoutingAlgorithm::V1(algorithm_id) = session_pm_input.routing_algorithm; + +// let profile_wrapper = admin::ProfileWrapper::new(business_profile.clone()); +// let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id { +// let cached_algorithm = ensure_algorithm_cached_v1( +// &session_pm_input.state.clone(), +// merchant_id, +// algorithm_id, +// session_pm_input.profile_id, +// transaction_type, +// ) +// .await?; + +// match cached_algorithm.as_ref() { +// CachedAlgorithm::Single(conn) => vec![(**conn).clone()], +// CachedAlgorithm::Priority(plist) => plist.clone(), +// CachedAlgorithm::VolumeSplit(splits) => { +// perform_volume_split(splits.to_vec(), Some(session_pm_input.attempt_id)) +// .change_context(errors::RoutingError::ConnectorSelectionFailed)? +// } +// CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1( +// session_pm_input.backend_input.clone(), +// interpreter, +// )?, +// } +// } else { +// profile_wrapper +// .get_default_fallback_list_of_connector_under_profile() +// .change_context(errors::RoutingError::FallbackConfigFetchFailed)? +// }; + +// let mut final_selection = perform_cgraph_filtering( +// &session_pm_input.state.clone(), +// session_pm_input.key_store, +// chosen_connectors, +// session_pm_input.backend_input.clone(), +// None, +// session_pm_input.profile_id, +// transaction_type, +// ) +// .await?; + +// if final_selection.is_empty() { +// let fallback = profile_wrapper +// .get_default_fallback_list_of_connector_under_profile() +// .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; + +// final_selection = perform_cgraph_filtering( +// &session_pm_input.state.clone(), +// session_pm_input.key_store, +// fallback, +// session_pm_input.backend_input.clone(), +// None, +// session_pm_input.profile_id, +// transaction_type, +// ) +// .await?; +// } + +// if final_selection.is_empty() { +// Ok(None) +// } else { +// Ok(Some(final_selection)) +// } +// } #[cfg(feature = "v2")] pub fn make_dsl_input_for_surcharge( _payment_attempt: &oss_storage::PaymentAttempt, diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 61ebffe6b83a..22354b4c00fc 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -329,7 +329,7 @@ pub async fn initiate_payout_link( } } -#[cfg(feature = "payouts")] +#[cfg(all(feature = "payouts", feature = "v1"))] pub async fn filter_payout_methods( state: &SessionState, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 609414c2920c..b2d6b2ace45c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -84,7 +84,7 @@ pub fn get_next_connector( .attach_printable("Connector not found in connectors iterator") } -#[cfg(feature = "payouts")] +#[cfg(all(feature = "payouts", feature = "v1"))] pub async fn get_connector_choice( state: &SessionState, merchant_account: &domain::MerchantAccount, @@ -263,6 +263,7 @@ pub async fn make_connector_decision( } } +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn payouts_core( state: &SessionState, @@ -297,6 +298,19 @@ pub async fn payouts_core( .await } +#[cfg(feature = "v2")] +#[instrument(skip_all)] +pub async fn payouts_core( + state: &SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + payout_data: &mut PayoutData, + routing_algorithm: Option, + eligible_connectors: Option>, +) -> RouterResult<()> { + todo!() +} + #[instrument(skip_all)] pub async fn payouts_create_core( state: SessionState, @@ -517,6 +531,7 @@ pub async fn payouts_update_core( response_handler(&state, &merchant_account, &payout_data).await } +#[cfg(all(feature = "payouts", feature = "v1"))] #[instrument(skip_all)] pub async fn payouts_retrieve_core( state: SessionState, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index de287defa868..b07094c321cc 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -769,6 +769,7 @@ pub(super) async fn get_or_create_customer_details( } } +#[cfg(all(feature = "payouts", feature = "v1"))] pub async fn decide_payout_connector( state: &SessionState, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 4155bbf3ff9a..525c5f12dc4e 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1228,6 +1228,23 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all(feature = "oltp", feature = "v2"))] + async fn list_enabled_connector_accounts_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + connector_type: common_enums::ConnectorType, + ) -> CustomResult, errors::StorageError> { + self.list_enabled_connector_accounts_by_profile_id( + state, + profile_id, + key_store, + connector_type, + ) + .await + } + async fn insert_merchant_connector_account( &self, state: &KeyManagerState, diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index 687f6e8fea24..0abbccd2cb35 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -189,6 +189,15 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; + #[cfg(all(feature = "oltp", feature = "v2"))] + async fn list_enabled_connector_accounts_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &common_utils::id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + connector_type: common_enums::ConnectorType, + ) -> CustomResult, errors::StorageError>; + async fn update_merchant_connector_account( &self, state: &KeyManagerState, @@ -493,6 +502,41 @@ impl MerchantConnectorAccountInterface for Store { .await } + #[cfg(all(feature = "oltp", feature = "v2"))] + async fn list_enabled_connector_accounts_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &common_utils::id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + connector_type: common_enums::ConnectorType, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + + storage::MerchantConnectorAccount::list_enabled_by_profile_id( + &conn, + profile_id, + connector_type, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + .async_and_then(|items| async { + let mut output = Vec::with_capacity(items.len()); + for item in items.into_iter() { + output.push( + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, + ) + } + Ok(output) + }) + .await + } + #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, @@ -1010,6 +1054,17 @@ impl MerchantConnectorAccountInterface for MockDb { } } + #[cfg(all(feature = "oltp", feature = "v2"))] + async fn list_enabled_connector_accounts_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &common_utils::id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + connector_type: common_enums::ConnectorType, + ) -> CustomResult, errors::StorageError> { + todo!() + } + #[cfg(feature = "v1")] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 2decddb806cb..0766382b6603 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -574,8 +574,8 @@ impl Payments { .route(web::get().to(payments::payments_start_redirection)), ) .service( - web::resource("/saved-payment-methods") - .route(web::get().to(list_customer_payment_method_for_payment)), + web::resource("/payment-methods") + .route(web::get().to(payments::list_payment_methods)), ) .service( web::resource("/finish-redirection/{publishable_key}/{profile_id}") diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 729a35aa8b7a..d8dfb7320618 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -511,45 +511,6 @@ pub async fn list_customer_payment_method_api( .await } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[instrument(skip_all, fields(flow = ?Flow::CustomerPaymentMethodsList))] -pub async fn list_customer_payment_method_for_payment( - state: web::Data, - payment_id: web::Path<(String,)>, - req: HttpRequest, - query_payload: web::Query, -) -> HttpResponse { - let flow = Flow::CustomerPaymentMethodsList; - let payload = query_payload.into_inner(); - let _payment_id = payment_id.into_inner().0.clone(); - - let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) { - Ok((auth, _auth_flow)) => (auth, _auth_flow), - Err(e) => return api::log_and_return_error_response(e), - }; - - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { - list_customer_payment_method_util( - state, - auth.merchant_account, - auth.profile, - auth.key_store, - Some(req), - None, - true, - ) - }, - &*auth, - api_locking::LockAction::NotApplicable, - )) - .await -} - #[cfg(all( feature = "v2", feature = "payment_methods_v2", diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index a9005644bebb..fdc7d9ced072 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -2519,3 +2519,51 @@ pub async fn payments_capture( )) .await } + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsList))] +pub async fn list_payment_methods( + state: web::Data, + req: actix_web::HttpRequest, + path: web::Path, + query_payload: web::Query, +) -> impl Responder { + let flow = Flow::PaymentMethodsList; + let payload = query_payload.into_inner(); + let global_payment_id = path.into_inner(); + + tracing::Span::current().record("payment_id", global_payment_id.get_string_repr()); + + let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId { + global_payment_id, + payload, + }; + + let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { + Ok(headers) => headers, + Err(err) => { + return api::log_and_return_error_response(err); + } + }; + + Box::pin(api::server_wrap( + flow, + state, + &req, + internal_payload, + |state, auth, req, _| { + payments::payment_methods::list_payment_methods( + state, + auth.merchant_account, + auth.profile, + auth.key_store, + req.global_payment_id, + req.payload, + &header_payload, + ) + }, + &auth::PublishableKeyAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 1d9a86f0aff9..bbcdfc535df8 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1000,6 +1000,7 @@ impl ForeignTryFrom { type Error = error_stack::Report; fn foreign_try_from(item: domain::MerchantConnectorAccount) -> Result { + #[cfg(feature = "v1")] let payment_methods_enabled = match item.payment_methods_enabled { Some(secret_val) => { let val = secret_val @@ -1012,6 +1013,7 @@ impl ForeignTryFrom } None => None, }; + let frm_configs = match item.frm_configs { Some(frm_value) => { let configs_for_frm : Vec = frm_value @@ -1055,7 +1057,7 @@ impl ForeignTryFrom connector_name: item.connector_name, connector_label: item.connector_label, disabled: item.disabled, - payment_methods_enabled, + payment_methods_enabled: item.payment_methods_enabled, frm_configs, profile_id: item.profile_id, applepay_verified_domains: item.applepay_verified_domains, @@ -1066,6 +1068,7 @@ impl ForeignTryFrom } } +#[cfg(feature = "v1")] impl ForeignTryFrom for api_models::admin::MerchantConnectorResponse { @@ -1229,6 +1232,103 @@ impl ForeignTryFrom } } +#[cfg(feature = "v2")] +impl ForeignTryFrom + for api_models::admin::MerchantConnectorResponse +{ + type Error = error_stack::Report; + fn foreign_try_from(item: domain::MerchantConnectorAccount) -> Result { + let frm_configs = match item.frm_configs { + Some(ref frm_value) => { + let configs_for_frm : Vec = frm_value + .iter() + .map(|config| { config + .peek() + .clone() + .parse_value("FrmConfigs") + .change_context(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "frm_configs".to_string(), + expected_format: r#"[{ "gateway": "stripe", "payment_methods": [{ "payment_method": "card","payment_method_types": [{"payment_method_type": "credit","card_networks": ["Visa"],"flow": "pre","action": "cancel_txn"}]}]}]"#.to_string(), + }) + }) + .collect::, _>>()?; + Some(configs_for_frm) + } + None => None, + }; + + // parse the connector_account_details into ConnectorAuthType + let connector_account_details: hyperswitch_domain_models::router_data::ConnectorAuthType = + item.connector_account_details + .clone() + .into_inner() + .parse_value("ConnectorAuthType") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while parsing value for ConnectorAuthType")?; + // get the masked keys from the ConnectorAuthType and encode it to secret value + let masked_connector_account_details = Secret::new( + connector_account_details + .get_masked_keys() + .encode_to_value() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode ConnectorAuthType")?, + ); + + let response = Self { + id: item.get_id(), + connector_type: item.connector_type, + connector_name: item.connector_name, + connector_label: item.connector_label, + connector_account_details: masked_connector_account_details, + disabled: item.disabled, + payment_methods_enabled: item.payment_methods_enabled, + metadata: item.metadata, + frm_configs, + connector_webhook_details: item + .connector_webhook_details + .map(|webhook_details| { + serde_json::Value::parse_value( + webhook_details.expose(), + "MerchantConnectorWebhookDetails", + ) + .attach_printable("Unable to deserialize connector_webhook_details") + .change_context(errors::ApiErrorResponse::InternalServerError) + }) + .transpose()?, + 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), + connector_wallets_details: item + .connector_wallets_details + .map(|data| { + data.into_inner() + .expose() + .parse_value::( + "ConnectorWalletDetails", + ) + .attach_printable("Unable to deserialize connector_wallets_details") + .change_context(errors::ApiErrorResponse::InternalServerError) + }) + .transpose()?, + }; + Ok(response) + } +} + #[cfg(feature = "v1")] impl ForeignFrom for payments::PaymentAttemptResponse { fn foreign_from(payment_attempt: storage::PaymentAttempt) -> Self { diff --git a/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql b/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql index d008367af333..a4616016abfc 100644 --- a/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql +++ b/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql @@ -43,3 +43,6 @@ ALTER TABLE payment_attempt DROP COLUMN payment_method_type_v2, DROP COLUMN payment_method_billing_address, DROP COLUMN redirection_data, DROP COLUMN connector_payment_data; + +ALTER TABLE merchant_connector_account +ALTER COLUMN payment_methods_enabled TYPE JSON [ ]; diff --git a/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql b/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql index a402beac139c..faebb36cdf9d 100644 --- a/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql +++ b/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql @@ -52,3 +52,7 @@ ADD COLUMN payment_method_type_v2 VARCHAR, ADD COLUMN payment_method_billing_address BYTEA, ADD COLUMN redirection_data JSONB, ADD COLUMN connector_payment_data VARCHAR(512); + +-- Change the type of the column from JSON to JSONB +ALTER TABLE merchant_connector_account +ALTER COLUMN payment_methods_enabled TYPE JSONB [ ]; diff --git a/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql b/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql index 507b29dadf7d..276bea10bd51 100644 --- a/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql +++ b/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql @@ -33,7 +33,7 @@ ALTER TABLE business_profile DROP COLUMN profile_id, DROP COLUMN frm_routing_algorithm, DROP COLUMN payout_routing_algorithm; --- This migration is to remove the business_country, business_label, business_sub_label, test_mode, merchant_connector_id and frm_configs columns from the merchant_connector_account table +-- This migration is to remove the fields that are no longer used by the v1 application, or some type changes. ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS business_country, DROP COLUMN IF EXISTS business_label, DROP COLUMN IF EXISTS business_sub_label,