From bc6c460ca9767d3b80604166ce2cbd98550d175a Mon Sep 17 00:00:00 2001 From: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:38:46 +0530 Subject: [PATCH 01/48] feat(router): add admin list apis for v2 (#5883) --- api-reference-v2/openapi_spec.json | 139 +++++++++++++++++- crates/api_models/src/admin.rs | 2 +- .../src/query/merchant_account.rs | 2 +- .../src/query/merchant_connector_account.rs | 14 ++ crates/openapi/src/openapi.rs | 2 +- crates/openapi/src/openapi_v2.rs | 3 + crates/openapi/src/routes/business_profile.rs | 21 +++ crates/openapi/src/routes/merchant_account.rs | 24 ++- .../src/routes/merchant_connector_account.rs | 2 +- crates/openapi/src/routes/organization.rs | 18 +++ crates/router/src/core/admin.rs | 60 +++++++- crates/router/src/core/user.rs | 5 +- crates/router/src/db/kafka_store.rs | 14 +- crates/router/src/db/merchant_account.rs | 6 +- .../src/db/merchant_connector_account.rs | 72 +++++++++ crates/router/src/routes/admin.rs | 80 +++++++++- crates/router/src/routes/app.rs | 37 +++-- crates/router/src/utils/user_role.rs | 3 +- .../down.sql | 2 + .../up.sql | 2 + 20 files changed, 474 insertions(+), 34 deletions(-) create mode 100644 v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql create mode 100644 v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index f7aa7923b0f..94cffcd75ac 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -164,6 +164,50 @@ ] } }, + "/v2/organization/{organization_id}/merchant_accounts": { + "get": { + "tags": [ + "Organization" + ], + "summary": "Merchant Account - List", + "description": "List merchant accounts for an Organization", + "operationId": "List Merchant Accounts", + "parameters": [ + { + "name": "organization_id", + "in": "path", + "description": "The unique identifier for the Organization", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Merchant Account list retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantAccountResponse" + } + } + } + } + }, + "400": { + "description": "Invalid data" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, "/v2/connector_accounts": { "post": { "tags": [ @@ -401,7 +445,7 @@ ] } }, - "/v2/accounts": { + "/v2/merchant_accounts": { "post": { "tags": [ "Merchant Account" @@ -469,7 +513,7 @@ ] } }, - "/v2/accounts/{id}": { + "/v2/merchant_accounts/{id}": { "get": { "tags": [ "Merchant Account" @@ -575,6 +619,50 @@ ] } }, + "/v2/merchant_accounts/{account_id}/profiles": { + "get": { + "tags": [ + "Merchant Account" + ], + "summary": "Business Profile - List", + "description": "List business profiles for an Merchant", + "operationId": "List Business Profiles", + "parameters": [ + { + "name": "account_id", + "in": "path", + "description": "The unique identifier for the Merchant", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Business profile list retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BusinessProfileResponse" + } + } + } + } + }, + "400": { + "description": "Invalid data" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, "/v2/profiles": { "post": { "tags": [ @@ -719,6 +807,53 @@ ] } }, + "/v2/profiles/{profile_id}/connector_accounts": { + "get": { + "tags": [ + "Business Profile" + ], + "summary": "Merchant Connector - List", + "description": "List Merchant Connector Details for the business profile", + "operationId": "List all Merchant Connectors", + "parameters": [ + { + "name": "profile_id", + "in": "path", + "description": "The unique identifier for the business profile", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Merchant Connector list retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MerchantConnectorResponse" + } + } + } + } + }, + "401": { + "description": "Unauthorized request" + }, + "404": { + "description": "Merchant Connector does not exist in records" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, "/v2/profiles/{profile_id}/activate_routing_algorithm": { "patch": { "tags": [ diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index ef00c22433c..1846ef0e45a 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -26,7 +26,7 @@ use crate::{ #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] pub struct MerchantAccountListRequest { - pub organization_id: String, + pub organization_id: id_type::OrganizationId, } #[cfg(feature = "v1")] diff --git a/crates/diesel_models/src/query/merchant_account.rs b/crates/diesel_models/src/query/merchant_account.rs index e6aa5c25c23..fd5a171b7eb 100644 --- a/crates/diesel_models/src/query/merchant_account.rs +++ b/crates/diesel_models/src/query/merchant_account.rs @@ -91,7 +91,7 @@ impl MerchantAccount { pub async fn list_by_organization_id( conn: &PgPooledConn, - organization_id: &str, + organization_id: &common_utils::id_type::OrganizationId, ) -> StorageResult> { generics::generic_filter::< ::Table, diff --git a/crates/diesel_models/src/query/merchant_connector_account.rs b/crates/diesel_models/src/query/merchant_connector_account.rs index 0ebf0ab9c0b..0f095442004 100644 --- a/crates/diesel_models/src/query/merchant_connector_account.rs +++ b/crates/diesel_models/src/query/merchant_connector_account.rs @@ -227,4 +227,18 @@ impl MerchantConnectorAccount { .await } } + + pub async fn list_by_profile_id( + conn: &PgPooledConn, + profile_id: &common_utils::id_type::ProfileId, + ) -> StorageResult> { + generics::generic_filter::<::Table, _, _, _>( + conn, + dsl::profile_id.eq(profile_id.to_owned()), + None, + None, + Some(dsl::created_at.asc()), + ) + .await + } } diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 297228cc25e..d71757e8f34 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -104,7 +104,7 @@ Never share your secret api keys. Keep them guarded and secure. // Routes for merchant connector account routes::merchant_connector_account::connector_create, routes::merchant_connector_account::connector_retrieve, - routes::merchant_connector_account::payment_connector_list, + routes::merchant_connector_account::connector_list, routes::merchant_connector_account::connector_update, routes::merchant_connector_account::connector_delete, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 31feb8e85b1..f2a920cdb92 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -73,6 +73,7 @@ Never share your secret api keys. Keep them guarded and secure. routes::organization::organization_create, routes::organization::organization_retrieve, routes::organization::organization_update, + routes::organization::merchant_account_list, // Routes for merchant connector account routes::merchant_connector_account::connector_create, @@ -84,11 +85,13 @@ Never share your secret api keys. Keep them guarded and secure. routes::merchant_account::merchant_account_create, routes::merchant_account::merchant_account_retrieve, routes::merchant_account::merchant_account_update, + routes::merchant_account::business_profiles_list, // Routes for business profile routes::business_profile::business_profile_create, routes::business_profile::business_profile_retrieve, routes::business_profile::business_profile_update, + routes::business_profile::connector_list, // Routes for routing under business profile routes::business_profile::routing_link_config, diff --git a/crates/openapi/src/routes/business_profile.rs b/crates/openapi/src/routes/business_profile.rs index 07442f9757a..89003da08ee 100644 --- a/crates/openapi/src/routes/business_profile.rs +++ b/crates/openapi/src/routes/business_profile.rs @@ -323,3 +323,24 @@ pub async fn routing_update_default_config() {} security(("api_key" = []), ("jwt_key" = [])) )] pub async fn routing_retrieve_default_config() {} + +/// Merchant Connector - List +/// +/// List Merchant Connector Details for the business profile +#[utoipa::path( + get, + path = "/v2/profiles/{profile_id}/connector_accounts", + params( + ("profile_id" = String, Path, description = "The unique identifier for the business profile"), + ), + responses( + (status = 200, description = "Merchant Connector list retrieved successfully", body = Vec), + (status = 404, description = "Merchant Connector does not exist in records"), + (status = 401, description = "Unauthorized request") + ), + tag = "Business Profile", + operation_id = "List all Merchant Connectors", + security(("admin_api_key" = [])) +)] +#[cfg(feature = "v2")] +pub async fn connector_list() {} diff --git a/crates/openapi/src/routes/merchant_account.rs b/crates/openapi/src/routes/merchant_account.rs index 7e5106d1321..16533c6a61c 100644 --- a/crates/openapi/src/routes/merchant_account.rs +++ b/crates/openapi/src/routes/merchant_account.rs @@ -50,7 +50,7 @@ pub async fn merchant_account_create() {} /// Before creating the merchant account, it is mandatory to create an organization. #[utoipa::path( post, - path = "/v2/accounts", + path = "/v2/merchant_accounts", request_body( content = MerchantAccountCreate, examples( @@ -124,7 +124,7 @@ pub async fn retrieve_merchant_account() {} /// Retrieve a *merchant* account details. #[utoipa::path( get, - path = "/v2/accounts/{id}", + path = "/v2/merchant_accounts/{id}", params (("id" = String, Path, description = "The unique identifier for the merchant account")), responses( (status = 200, description = "Merchant Account Retrieved", body = MerchantAccountResponse), @@ -186,7 +186,7 @@ pub async fn update_merchant_account() {} /// Updates details of an existing merchant account. Helpful in updating merchant details such as email, contact details, or other configuration details like webhook, routing algorithm etc #[utoipa::path( put, - path = "/v2/accounts/{id}", + path = "/v2/merchant_accounts/{id}", request_body ( content = MerchantAccountUpdate, examples( @@ -269,3 +269,21 @@ pub async fn delete_merchant_account() {} security(("admin_api_key" = [])) )] pub async fn merchant_account_kv_status() {} + +#[cfg(feature = "v2")] +/// Business Profile - List +/// +/// List business profiles for an Merchant +#[utoipa::path( + get, + path = "/v2/merchant_accounts/{account_id}/profiles", + params (("account_id" = String, Path, description = "The unique identifier for the Merchant")), + responses( + (status = 200, description = "Business profile list retrieved successfully", body = Vec), + (status = 400, description = "Invalid data") + ), + tag = "Merchant Account", + operation_id = "List Business Profiles", + security(("admin_api_key" = [])) +)] +pub async fn business_profiles_list() {} diff --git a/crates/openapi/src/routes/merchant_connector_account.rs b/crates/openapi/src/routes/merchant_connector_account.rs index 55c6d88aac5..a7c91ce128c 100644 --- a/crates/openapi/src/routes/merchant_connector_account.rs +++ b/crates/openapi/src/routes/merchant_connector_account.rs @@ -185,7 +185,7 @@ pub async fn connector_retrieve() {} operation_id = "List all Merchant Connectors", security(("admin_api_key" = [])) )] -pub async fn payment_connector_list() {} +pub async fn connector_list() {} /// Merchant Connector - Update /// diff --git a/crates/openapi/src/routes/organization.rs b/crates/openapi/src/routes/organization.rs index c31ff70a79f..44d5f943574 100644 --- a/crates/openapi/src/routes/organization.rs +++ b/crates/openapi/src/routes/organization.rs @@ -143,3 +143,21 @@ pub async fn organization_retrieve() {} security(("admin_api_key" = [])) )] pub async fn organization_update() {} + +#[cfg(feature = "v2")] +/// Merchant Account - List +/// +/// List merchant accounts for an Organization +#[utoipa::path( + get, + path = "/v2/organization/{organization_id}/merchant_accounts", + params (("organization_id" = String, Path, description = "The unique identifier for the Organization")), + responses( + (status = 200, description = "Merchant Account list retrieved successfully", body = Vec), + (status = 400, description = "Invalid data") + ), + tag = "Organization", + operation_id = "List Merchant Accounts", + security(("admin_api_key" = [])) +)] +pub async fn merchant_account_list() {} diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 8ed2eb48b1b..690d16596b8 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -674,7 +674,35 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { } } -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v2"))] +pub async fn list_merchant_account( + state: SessionState, + organization_id: api_models::organization::OrganizationId, +) -> RouterResponse> { + let merchant_accounts = state + .store + .list_merchant_accounts_by_organization_id( + &(&state).into(), + &organization_id.organization_id, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + + let merchant_accounts = merchant_accounts + .into_iter() + .map(|merchant_account| { + api::MerchantAccountResponse::foreign_try_from(merchant_account).change_context( + errors::ApiErrorResponse::InvalidDataValue { + field_name: "merchant_account", + }, + ) + }) + .collect::, _>>()?; + + Ok(services::ApplicationResponse::Json(merchant_accounts)) +} + +#[cfg(all(feature = "olap", feature = "v1"))] pub async fn list_merchant_account( state: SessionState, req: api_models::admin::MerchantAccountListRequest, @@ -2920,6 +2948,36 @@ pub async fn retrieve_connector( )) } +#[cfg(all(feature = "olap", feature = "v2"))] +pub async fn list_connectors_for_a_profile( + state: SessionState, + merchant_account: domain::MerchantAccount, + profile_id: id_type::ProfileId, +) -> RouterResponse> { + let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); + let key_store = store + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_account.get_id(), + &store.get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + + let merchant_connector_accounts = store + .list_connector_account_by_profile_id(key_manager_state, &profile_id, &key_store) + .await + .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?; + let mut response = vec![]; + + for mca in merchant_connector_accounts.into_iter() { + response.push(mca.foreign_try_into()?); + } + + Ok(service_api::ApplicationResponse::Json(response)) +} + pub async fn list_payment_connectors( state: SessionState, merchant_id: id_type::MerchantId, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 45e9065d263..9d0f168827e 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -2681,10 +2681,7 @@ pub async fn list_merchants_for_user_in_org( let merchant_accounts = match role_info.get_entity_type() { EntityType::Organization | EntityType::Internal => state .store - .list_merchant_accounts_by_organization_id( - &(&state).into(), - user_from_token.org_id.get_string_repr(), - ) + .list_merchant_accounts_by_organization_id(&(&state).into(), &user_from_token.org_id) .await .change_context(UserErrors::InternalServerError)?, EntityType::Merchant | EntityType::Profile => { diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 6c620c4fcdc..4892e2552ec 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1008,7 +1008,7 @@ impl MerchantAccountInterface for KafkaStore { async fn list_merchant_accounts_by_organization_id( &self, state: &KeyManagerState, - organization_id: &str, + organization_id: &id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_merchant_accounts_by_organization_id(state, organization_id) @@ -1225,6 +1225,18 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } + #[cfg(all(feature = "olap", feature = "v2"))] + async fn list_connector_account_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult, errors::StorageError> { + self.diesel_store + .list_connector_account_by_profile_id(state, profile_id, key_store) + .await + } + async fn update_merchant_connector_account( &self, state: &KeyManagerState, diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index c3b2c45f335..13a778001ae 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -74,7 +74,7 @@ where async fn list_merchant_accounts_by_organization_id( &self, state: &KeyManagerState, - organization_id: &str, + organization_id: &common_utils::id_type::OrganizationId, ) -> CustomResult, errors::StorageError>; async fn delete_merchant_account_by_merchant_id( @@ -282,7 +282,7 @@ impl MerchantAccountInterface for Store { async fn list_merchant_accounts_by_organization_id( &self, state: &KeyManagerState, - organization_id: &str, + organization_id: &common_utils::id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { use futures::future::try_join_all; let conn = connection::pg_connection_read(self).await?; @@ -555,7 +555,7 @@ impl MerchantAccountInterface for MockDb { async fn list_merchant_accounts_by_organization_id( &self, _state: &KeyManagerState, - _organization_id: &str, + _organization_id: &common_utils::id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { Err(errors::StorageError::MockDbError)? } diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index c13fd3fe9b5..40e53572498 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -181,6 +181,14 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; + #[cfg(all(feature = "olap", feature = "v2"))] + async fn list_connector_account_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &common_utils::id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult, errors::StorageError>; + async fn update_merchant_connector_account( &self, state: &KeyManagerState, @@ -515,6 +523,36 @@ impl MerchantConnectorAccountInterface for Store { .await } + #[instrument(skip_all)] + #[cfg(all(feature = "olap", feature = "v2"))] + async fn list_connector_account_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &common_utils::id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + storage::MerchantConnectorAccount::list_by_profile_id(&conn, profile_id) + .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 update_multiple_merchant_connector_accounts( &self, @@ -1252,6 +1290,40 @@ impl MerchantConnectorAccountInterface for MockDb { Ok(output) } + #[cfg(all(feature = "olap", feature = "v2"))] + async fn list_connector_account_by_profile_id( + &self, + state: &KeyManagerState, + profile_id: &common_utils::id_type::ProfileId, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult, errors::StorageError> { + let accounts = self + .merchant_connector_accounts + .lock() + .await + .iter() + .filter(|account: &&storage::MerchantConnectorAccount| { + account.profile_id == *profile_id + }) + .cloned() + .collect::>(); + + let mut output = Vec::with_capacity(accounts.len()); + for account in accounts.into_iter() { + output.push( + account + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, + ) + } + Ok(output) + } + #[cfg(feature = "v1")] async fn update_merchant_connector_account( &self, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 0dc34aa4245..a68f648e178 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -128,7 +128,39 @@ pub async fn retrieve_merchant_account( .await } -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v2"))] +#[instrument(skip_all, fields(flow = ?Flow::MerchantAccountList))] +pub async fn merchant_account_list( + state: web::Data, + req: HttpRequest, + organization_id: web::Path, +) -> HttpResponse { + let flow = Flow::MerchantAccountList; + + let organization_id = admin::OrganizationId { + organization_id: organization_id.into_inner(), + }; + + Box::pin(api::server_wrap( + flow, + state, + &req, + organization_id, + |state, _, request, _| list_merchant_account(state, request), + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromHeader { + required_permission: Permission::MerchantAccountRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all, fields(flow = ?Flow::MerchantAccountList))] pub async fn merchant_account_list( state: web::Data, @@ -143,7 +175,14 @@ pub async fn merchant_account_list( &req, query_params.into_inner(), |state, _, request, _| list_merchant_account(state, request), - &auth::AdminApiAuth, + auth::auth_type( + &auth::AdminApiAuth, + &auth::JWTAuthMerchantFromHeader { + required_permission: Permission::MerchantAccountRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), api_locking::LockAction::NotApplicable, )) .await @@ -387,6 +426,38 @@ pub async fn connector_retrieve( ) .await } + +#[cfg(all(feature = "olap", feature = "v2"))] +#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))] +pub async fn connector_list( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::MerchantConnectorsList; + let profile_id = path.into_inner(); + + api::server_wrap( + flow, + state, + &req, + profile_id.to_owned(), + |state, auth, _, _| { + list_connectors_for_a_profile(state, auth.merchant_account.clone(), profile_id.clone()) + }, + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromHeader { + required_permission: Permission::MerchantConnectorAccountRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + ) + .await +} + /// Merchant Connector - List /// /// List Merchant Connector Details for the merchant @@ -405,8 +476,9 @@ pub async fn connector_retrieve( operation_id = "List all Merchant Connectors", security(("admin_api_key" = [])) )] +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))] -pub async fn payment_connector_list( +pub async fn connector_list( state: web::Data, req: HttpRequest, path: web::Path, @@ -452,7 +524,7 @@ pub async fn payment_connector_list( security(("admin_api_key" = [])) )] #[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))] -pub async fn payment_connector_list_profile( +pub async fn connector_list_profile( state: web::Data, req: HttpRequest, path: web::Path, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index da1a50b82ee..d66dc8e51e2 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1190,9 +1190,16 @@ impl Organization { .app_data(web::Data::new(state)) .service(web::resource("").route(web::post().to(admin::organization_create))) .service( - web::resource("/{id}") - .route(web::get().to(admin::organization_retrieve)) - .route(web::put().to(admin::organization_update)), + web::scope("/{id}") + .service( + web::resource("") + .route(web::get().to(admin::organization_retrieve)) + .route(web::put().to(admin::organization_update)), + ) + .service( + web::resource("/merchant_accounts") + .route(web::get().to(admin::merchant_account_list)), + ), ) } } @@ -1202,13 +1209,20 @@ pub struct MerchantAccount; #[cfg(all(feature = "v2", feature = "olap"))] impl MerchantAccount { pub fn server(state: AppState) -> Scope { - web::scope("/v2/accounts") + web::scope("/v2/merchant_accounts") .app_data(web::Data::new(state)) .service(web::resource("").route(web::post().to(admin::merchant_account_create))) .service( - web::resource("/{id}") - .route(web::get().to(admin::retrieve_merchant_account)) - .route(web::put().to(admin::update_merchant_account)), + web::scope("/{id}") + .service( + web::resource("") + .route(web::get().to(admin::retrieve_merchant_account)) + .route(web::put().to(admin::update_merchant_account)), + ) + .service( + web::resource("/profiles") + .route(web::get().to(admin::business_profiles_list)), + ), ) } } @@ -1282,7 +1296,7 @@ impl MerchantConnectorAccount { .service( web::resource("/{merchant_id}/connectors") .route(web::post().to(connector_create)) - .route(web::get().to(payment_connector_list)), + .route(web::get().to(connector_list)), ) .service( web::resource("/{merchant_id}/connectors/{merchant_connector_id}") @@ -1570,6 +1584,10 @@ impl BusinessProfile { .route(web::get().to(super::admin::business_profile_retrieve)) .route(web::put().to(super::admin::business_profile_update)), ) + .service( + web::resource("/connector_accounts") + .route(web::get().to(admin::connector_list)), + ) .service( web::resource("/fallback_routing") .route(web::get().to(routing::routing_retrieve_default_config)) @@ -1673,8 +1691,7 @@ impl BusinessProfileNew { .route(web::get().to(admin::business_profiles_list_at_profile_level)), ) .service( - web::resource("/connectors") - .route(web::get().to(admin::payment_connector_list_profile)), + web::resource("/connectors").route(web::get().to(admin::connector_list_profile)), ) } } diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index 2d700ffe505..3936ff60b72 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -229,8 +229,7 @@ pub async fn get_single_merchant_id( .org_id .as_ref() .ok_or(UserErrors::InternalServerError) - .attach_printable("org_id not found")? - .get_string_repr(), + .attach_printable("org_id not found")?, ) .await .change_context(UserErrors::InternalServerError) diff --git a/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql b/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql new file mode 100644 index 00000000000..32225e3496b --- /dev/null +++ b/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP INDEX IF EXISTS merchant_connector_account_profile_id_index; \ No newline at end of file diff --git a/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql b/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql new file mode 100644 index 00000000000..f4ca993fef4 --- /dev/null +++ b/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +CREATE INDEX IF NOT EXISTS merchant_connector_account_profile_id_index ON merchant_connector_account (profile_id); From f72abe4b979873b06d75553c7412f8072c29c8a9 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:26:08 +0530 Subject: [PATCH 02/48] refactor: add encryption support to payment attempt domain model (#5882) --- .github/workflows/CI-pr.yml | 11 +- crates/diesel_models/src/enums.rs | 2 +- crates/diesel_models/src/kv.rs | 12 + crates/diesel_models/src/payment_attempt.rs | 232 +- .../src/query/payment_attempt.rs | 29 + crates/diesel_models/src/user/sample_data.rs | 14 +- .../hyperswitch_domain_models/src/mandates.rs | 62 + .../src/payments/payment_attempt.rs | 1862 ++++++++++++++--- .../src/payments/payment_intent.rs | 442 +++- crates/router/src/core/customers.rs | 14 +- crates/router/src/core/fraud_check.rs | 1 + .../fraud_check/operation/fraud_check_post.rs | 30 +- .../router/src/core/payment_methods/cards.rs | 17 +- crates/router/src/core/payments/helpers.rs | 52 +- .../payments/operations/payment_create.rs | 22 +- .../payments/operations/payment_response.rs | 54 +- crates/router/src/core/payments/retry.rs | 163 +- crates/router/src/core/payments/routing.rs | 6 +- crates/router/src/db/kafka_store.rs | 96 +- crates/router/src/db/mandate.rs | 8 +- crates/router/src/db/payment_method.rs | 8 +- crates/router/src/routes/app.rs | 6 +- crates/router/src/types/api/admin.rs | 2 +- crates/router/src/types/domain/user.rs | 2 +- .../src/types/storage/payment_attempt.rs | 6 +- crates/router/src/types/transformers.rs | 5 +- crates/router/src/utils.rs | 17 +- crates/router/src/utils/user/sample_data.rs | 6 +- .../src/mock_db/payment_attempt.rs | 56 + .../src/payments/payment_attempt.rs | 370 +++- 30 files changed, 2929 insertions(+), 678 deletions(-) diff --git a/.github/workflows/CI-pr.yml b/.github/workflows/CI-pr.yml index a31d0450010..4ea196e1c22 100644 --- a/.github/workflows/CI-pr.yml +++ b/.github/workflows/CI-pr.yml @@ -309,12 +309,15 @@ jobs: - name: Run cargo check with v2 features enabled shell: bash - # env: - # # Not denying warnings for now. - # # We only want to ensure successful compilation for now. - # RUSTFLAGS: "-D warnings -A clippy::todo" + env: + RUSTFLAGS: "-A warnings" + # Not denying warnings for now. + # We only want to ensure successful compilation for now. + # RUSTFLAGS: "-D warnings -A clippy::todo" run: just check_v2 - name: Run cargo check enabling only the release and v2 features shell: bash + env: + RUSTFLAGS: "-A warnings" run: cargo check --no-default-features --features "release,v2" diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 68809d8d2fa..77d167402ef 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -172,7 +172,7 @@ common_utils::impl_to_sql_from_sql_json!(MandateDataType); #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct MandateAmountData { - pub amount: i64, + pub amount: common_utils::types::MinorUnit, pub currency: Currency, pub start_date: Option, pub end_date: Option, diff --git a/crates/diesel_models/src/kv.rs b/crates/diesel_models/src/kv.rs index c8940c4c6b2..1cf7fc9d81c 100644 --- a/crates/diesel_models/src/kv.rs +++ b/crates/diesel_models/src/kv.rs @@ -1,6 +1,8 @@ use error_stack::ResultExt; use serde::{Deserialize, Serialize}; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use crate::payment_attempt::PaymentAttemptUpdateInternal; use crate::{ address::{Address, AddressNew, AddressUpdateInternal}, customers::{Customer, CustomerNew, CustomerUpdateInternal}, @@ -109,9 +111,19 @@ impl DBOperation { Updateable::PaymentIntentUpdate(a) => { DBResult::PaymentIntent(Box::new(a.orig.update(conn, a.update_data).await?)) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] Updateable::PaymentAttemptUpdate(a) => DBResult::PaymentAttempt(Box::new( a.orig.update_with_attempt_id(conn, a.update_data).await?, )), + #[cfg(all(feature = "v2", feature = "payment_v2"))] + Updateable::PaymentAttemptUpdate(a) => DBResult::PaymentAttempt(Box::new( + a.orig + .update_with_attempt_id( + conn, + PaymentAttemptUpdateInternal::from(a.update_data), + ) + .await?, + )), Updateable::RefundUpdate(a) => { DBResult::Refund(Box::new(a.orig.update(conn, a.update_data).await?)) } diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index 152037d55fe..63ce905f65f 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -19,14 +19,14 @@ pub struct PaymentAttempt { pub merchant_id: id_type::MerchantId, pub attempt_id: String, pub status: storage_enums::AttemptStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, pub save_to_locker: Option, pub connector: Option, pub error_message: Option, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option, pub payment_method: Option, pub connector_transaction_id: Option, @@ -42,7 +42,7 @@ pub struct PaymentAttempt { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub cancellation_reason: Option, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option, pub browser_info: Option, pub error_code: Option, @@ -60,14 +60,14 @@ pub struct PaymentAttempt { pub multiple_capture_count: Option, // reference to the payment at connector side pub connector_response_reference_id: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub updated_by: String, pub merchant_connector_id: Option, pub authentication_data: Option, pub encoded_data: Option, pub unified_code: Option, pub unified_message: Option, - pub net_amount: Option, + pub net_amount: Option, pub external_three_ds_authentication_attempted: Option, pub authentication_connector: Option, pub authentication_id: Option, @@ -95,14 +95,14 @@ pub struct PaymentAttempt { pub merchant_id: id_type::MerchantId, pub attempt_id: String, pub status: storage_enums::AttemptStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, pub save_to_locker: Option, pub connector: Option, pub error_message: Option, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option, pub payment_method: Option, pub connector_transaction_id: Option, @@ -118,7 +118,7 @@ pub struct PaymentAttempt { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub cancellation_reason: Option, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option, pub browser_info: Option, pub error_code: Option, @@ -136,14 +136,14 @@ pub struct PaymentAttempt { pub multiple_capture_count: Option, // reference to the payment at connector side pub connector_response_reference_id: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub updated_by: String, pub merchant_connector_id: Option, pub authentication_data: Option, pub encoded_data: Option, pub unified_code: Option, pub unified_message: Option, - pub net_amount: Option, + pub net_amount: Option, pub external_three_ds_authentication_attempted: Option, pub authentication_connector: Option, pub authentication_id: Option, @@ -162,22 +162,13 @@ pub struct PaymentAttempt { } impl PaymentAttempt { - pub fn get_or_calculate_net_amount(&self) -> i64 { - let shipping_cost = self - .shipping_cost - .unwrap_or(MinorUnit::new(0)) - .get_amount_as_i64(); - let order_tax_amount = self - .order_tax_amount - .unwrap_or(MinorUnit::new(0)) - .get_amount_as_i64(); - + pub fn get_or_calculate_net_amount(&self) -> MinorUnit { self.net_amount.unwrap_or( self.amount - + self.surcharge_amount.unwrap_or(0) - + self.tax_amount.unwrap_or(0) - + shipping_cost - + order_tax_amount, + + self.surcharge_amount.unwrap_or(MinorUnit::new(0)) + + self.tax_amount.unwrap_or(MinorUnit::new(0)) + + self.shipping_cost.unwrap_or(MinorUnit::new(0)) + + self.order_tax_amount.unwrap_or(MinorUnit::new(0)), ) } } @@ -197,15 +188,15 @@ pub struct PaymentAttemptNew { pub merchant_id: id_type::MerchantId, pub attempt_id: String, pub status: storage_enums::AttemptStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, // pub auto_capture: Option, pub save_to_locker: Option, pub connector: Option, pub error_message: Option, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option, pub payment_method: Option, pub capture_method: Option, @@ -220,7 +211,7 @@ pub struct PaymentAttemptNew { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub cancellation_reason: Option, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option, pub browser_info: Option, pub payment_token: Option, @@ -236,14 +227,14 @@ pub struct PaymentAttemptNew { pub error_reason: Option, pub connector_response_reference_id: Option, pub multiple_capture_count: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub updated_by: String, pub merchant_connector_id: Option, pub authentication_data: Option, pub encoded_data: Option, pub unified_code: Option, pub unified_message: Option, - pub net_amount: Option, + pub net_amount: Option, pub external_three_ds_authentication_attempted: Option, pub authentication_connector: Option, pub authentication_id: Option, @@ -263,19 +254,14 @@ pub struct PaymentAttemptNew { impl PaymentAttemptNew { /// returns amount + surcharge_amount + tax_amount (surcharge) + shipping_cost + order_tax_amount - pub fn calculate_net_amount(&self) -> i64 { - let shipping_cost = self - .shipping_cost - .unwrap_or(MinorUnit::new(0)) - .get_amount_as_i64(); - + pub fn calculate_net_amount(&self) -> MinorUnit { self.amount - + self.surcharge_amount.unwrap_or(0) - + self.tax_amount.unwrap_or(0) - + shipping_cost + + self.surcharge_amount.unwrap_or(MinorUnit::new(0)) + + self.tax_amount.unwrap_or(MinorUnit::new(0)) + + self.shipping_cost.unwrap_or(MinorUnit::new(0)) } - pub fn get_or_calculate_net_amount(&self) -> i64 { + pub fn get_or_calculate_net_amount(&self) -> MinorUnit { self.net_amount .unwrap_or_else(|| self.calculate_net_amount()) } @@ -290,7 +276,7 @@ impl PaymentAttemptNew { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PaymentAttemptUpdate { Update { - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, status: storage_enums::AttemptStatus, authentication_type: Option, @@ -300,10 +286,10 @@ pub enum PaymentAttemptUpdate { payment_method_type: Option, payment_experience: Option, business_sub_label: Option, - amount_to_capture: Option, + amount_to_capture: Option, capture_method: Option, - surcharge_amount: Option, - tax_amount: Option, + surcharge_amount: Option, + tax_amount: Option, fingerprint_id: Option, payment_method_billing_address_id: Option, updated_by: String, @@ -312,9 +298,9 @@ pub enum PaymentAttemptUpdate { payment_token: Option, connector: Option, straight_through_algorithm: Option, - amount_capturable: Option, - surcharge_amount: Option, - tax_amount: Option, + amount_capturable: Option, + surcharge_amount: Option, + tax_amount: Option, updated_by: String, merchant_connector_id: Option, }, @@ -323,7 +309,7 @@ pub enum PaymentAttemptUpdate { updated_by: String, }, ConfirmUpdate { - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, status: storage_enums::AttemptStatus, authentication_type: Option, @@ -339,9 +325,9 @@ pub enum PaymentAttemptUpdate { straight_through_algorithm: Option, error_code: Option>, error_message: Option>, - amount_capturable: Option, - surcharge_amount: Option, - tax_amount: Option, + amount_capturable: Option, + surcharge_amount: Option, + tax_amount: Option, fingerprint_id: Option, updated_by: String, merchant_connector_id: Option, @@ -390,7 +376,7 @@ pub enum PaymentAttemptUpdate { error_message: Option>, error_reason: Option>, connector_response_reference_id: Option, - amount_capturable: Option, + amount_capturable: Option, updated_by: String, authentication_data: Option, encoded_data: Option, @@ -420,7 +406,7 @@ pub enum PaymentAttemptUpdate { error_code: Option>, error_message: Option>, error_reason: Option>, - amount_capturable: Option, + amount_capturable: Option, updated_by: String, unified_code: Option>, unified_message: Option>, @@ -429,13 +415,13 @@ pub enum PaymentAttemptUpdate { authentication_type: Option, }, CaptureUpdate { - amount_to_capture: Option, + amount_to_capture: Option, multiple_capture_count: Option, updated_by: String, }, AmountToCaptureUpdate { status: storage_enums::AttemptStatus, - amount_capturable: i64, + amount_capturable: MinorUnit, updated_by: String, }, PreprocessingUpdate { @@ -456,8 +442,8 @@ pub enum PaymentAttemptUpdate { updated_by: String, }, IncrementalAuthorizationAmountUpdate { - amount: i64, - amount_capturable: i64, + amount: MinorUnit, + amount_capturable: MinorUnit, }, AuthenticationUpdate { status: storage_enums::AttemptStatus, @@ -481,82 +467,78 @@ pub enum PaymentAttemptUpdate { #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = payment_attempt)] pub struct PaymentAttemptUpdateInternal { - amount: Option, - net_amount: Option, - currency: Option, - status: Option, - connector_transaction_id: Option, - amount_to_capture: Option, - connector: Option>, - authentication_type: Option, - payment_method: Option, - error_message: Option>, - payment_method_id: Option, - cancellation_reason: Option, - modified_at: PrimitiveDateTime, - mandate_id: Option, - browser_info: Option, - payment_token: Option, - error_code: Option>, - connector_metadata: Option, - payment_method_data: Option, - payment_method_type: Option, - payment_experience: Option, - business_sub_label: Option, - straight_through_algorithm: Option, - preprocessing_step_id: Option, - error_reason: Option>, - capture_method: Option, - connector_response_reference_id: Option, - multiple_capture_count: Option, - surcharge_amount: Option, - tax_amount: Option, - amount_capturable: Option, - updated_by: String, - merchant_connector_id: Option>, - authentication_data: Option, - encoded_data: Option, - unified_code: Option>, - unified_message: Option>, - external_three_ds_authentication_attempted: Option, - authentication_connector: Option, - authentication_id: Option, - fingerprint_id: Option, - payment_method_billing_address_id: Option, - charge_id: Option, - client_source: Option, - client_version: Option, - customer_acceptance: Option, - card_network: Option, - shipping_cost: Option, - order_tax_amount: Option, + pub amount: Option, + pub net_amount: Option, + pub currency: Option, + pub status: Option, + pub connector_transaction_id: Option, + pub amount_to_capture: Option, + pub connector: Option>, + pub authentication_type: Option, + pub payment_method: Option, + pub error_message: Option>, + pub payment_method_id: Option, + pub cancellation_reason: Option, + pub modified_at: PrimitiveDateTime, + pub mandate_id: Option, + pub browser_info: Option, + pub payment_token: Option, + pub error_code: Option>, + pub connector_metadata: Option, + pub payment_method_data: Option, + pub payment_method_type: Option, + pub payment_experience: Option, + pub business_sub_label: Option, + pub straight_through_algorithm: Option, + pub preprocessing_step_id: Option, + pub error_reason: Option>, + pub capture_method: Option, + pub connector_response_reference_id: Option, + pub multiple_capture_count: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, + pub amount_capturable: Option, + pub updated_by: String, + pub merchant_connector_id: Option>, + pub authentication_data: Option, + pub encoded_data: Option, + pub unified_code: Option>, + pub unified_message: Option>, + pub external_three_ds_authentication_attempted: Option, + pub authentication_connector: Option, + pub authentication_id: Option, + pub fingerprint_id: Option, + pub payment_method_billing_address_id: Option, + pub charge_id: Option, + pub client_source: Option, + pub client_version: Option, + pub customer_acceptance: Option, + pub card_network: Option, + pub shipping_cost: Option, + pub order_tax_amount: Option, } impl PaymentAttemptUpdateInternal { pub fn populate_derived_fields(self, source: &PaymentAttempt) -> Self { let mut update_internal = self; - let shipping_cost = update_internal - .shipping_cost - .or(source.shipping_cost) - .unwrap_or(MinorUnit::new(0)) - .get_amount_as_i64(); - let order_tax_amount = update_internal - .order_tax_amount - .or(source.order_tax_amount) - .unwrap_or(MinorUnit::new(0)) - .get_amount_as_i64(); update_internal.net_amount = Some( update_internal.amount.unwrap_or(source.amount) + update_internal .surcharge_amount .or(source.surcharge_amount) - .unwrap_or(0) + .unwrap_or(MinorUnit::new(0)) + update_internal .tax_amount .or(source.tax_amount) - .unwrap_or(0) - + shipping_cost - + order_tax_amount, + .unwrap_or(MinorUnit::new(0)) + + update_internal + .shipping_cost + .or(source.shipping_cost) + .unwrap_or(MinorUnit::new(0)) + + update_internal + .order_tax_amount + .or(source.order_tax_amount) + .unwrap_or(MinorUnit::new(0)), ); update_internal.card_network = update_internal .payment_method_data diff --git a/crates/diesel_models/src/query/payment_attempt.rs b/crates/diesel_models/src/query/payment_attempt.rs index a9d44bca88f..fa80b0990d1 100644 --- a/crates/diesel_models/src/query/payment_attempt.rs +++ b/crates/diesel_models/src/query/payment_attempt.rs @@ -29,6 +29,7 @@ impl PaymentAttemptNew { } impl PaymentAttempt { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] pub async fn update_with_attempt_id( self, conn: &PgPooledConn, @@ -56,6 +57,34 @@ impl PaymentAttempt { } } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + pub async fn update_with_attempt_id( + self, + conn: &PgPooledConn, + payment_attempt: PaymentAttemptUpdateInternal, + ) -> StorageResult { + match generics::generic_update_with_unique_predicate_get_result::< + ::Table, + _, + _, + _, + >( + conn, + dsl::attempt_id + .eq(self.attempt_id.to_owned()) + .and(dsl::merchant_id.eq(self.merchant_id.to_owned())), + payment_attempt.populate_derived_fields(&self), + ) + .await + { + Err(error) => match error.current_context() { + DatabaseError::NoFieldsToUpdate => Ok(self), + _ => Err(error), + }, + result => result, + } + } + pub async fn find_optional_by_payment_id_merchant_id( conn: &PgPooledConn, payment_id: &common_utils::id_type::PaymentId, diff --git a/crates/diesel_models/src/user/sample_data.rs b/crates/diesel_models/src/user/sample_data.rs index f4c8cf30fab..ddcf6352c52 100644 --- a/crates/diesel_models/src/user/sample_data.rs +++ b/crates/diesel_models/src/user/sample_data.rs @@ -24,14 +24,14 @@ pub struct PaymentAttemptBatchNew { pub merchant_id: common_utils::id_type::MerchantId, pub attempt_id: String, pub status: AttemptStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, pub save_to_locker: Option, pub connector: Option, pub error_message: Option, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option, pub payment_method: Option, pub capture_method: Option, @@ -46,7 +46,7 @@ pub struct PaymentAttemptBatchNew { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub cancellation_reason: Option, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option, pub browser_info: Option, pub payment_token: Option, @@ -63,14 +63,14 @@ pub struct PaymentAttemptBatchNew { pub connector_response_reference_id: Option, pub connector_transaction_id: Option, pub multiple_capture_count: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub updated_by: String, pub merchant_connector_id: Option, pub authentication_data: Option, pub encoded_data: Option, pub unified_code: Option, pub unified_message: Option, - pub net_amount: Option, + pub net_amount: Option, pub external_three_ds_authentication_attempted: Option, pub authentication_connector: Option, pub authentication_id: Option, diff --git a/crates/hyperswitch_domain_models/src/mandates.rs b/crates/hyperswitch_domain_models/src/mandates.rs index c1380fe51ce..d12b041a8ab 100644 --- a/crates/hyperswitch_domain_models/src/mandates.rs +++ b/crates/hyperswitch_domain_models/src/mandates.rs @@ -15,6 +15,22 @@ pub struct MandateDetails { pub update_mandate_id: Option, } +impl From for diesel_models::enums::MandateDetails { + fn from(value: MandateDetails) -> Self { + Self { + update_mandate_id: value.update_mandate_id, + } + } +} + +impl From for MandateDetails { + fn from(value: diesel_models::enums::MandateDetails) -> Self { + Self { + update_mandate_id: value.update_mandate_id, + } + } +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum MandateDataType { @@ -84,6 +100,28 @@ impl From for MandateDataType { } } +impl From for diesel_models::enums::MandateDataType { + fn from(value: MandateDataType) -> Self { + match value { + MandateDataType::SingleUse(data) => Self::SingleUse(data.into()), + MandateDataType::MultiUse(None) => Self::MultiUse(None), + MandateDataType::MultiUse(Some(data)) => Self::MultiUse(Some(data.into())), + } + } +} + +impl From for MandateDataType { + fn from(value: diesel_models::enums::MandateDataType) -> Self { + use diesel_models::enums::MandateDataType as DieselMandateDataType; + + match value { + DieselMandateDataType::SingleUse(data) => Self::SingleUse(data.into()), + DieselMandateDataType::MultiUse(None) => Self::MultiUse(None), + DieselMandateDataType::MultiUse(Some(data)) => Self::MultiUse(Some(data.into())), + } + } +} + impl From for MandateAmountData { fn from(value: ApiMandateAmountData) -> Self { Self { @@ -96,6 +134,30 @@ impl From for MandateAmountData { } } +impl From for diesel_models::enums::MandateAmountData { + fn from(value: MandateAmountData) -> Self { + Self { + amount: value.amount, + currency: value.currency, + start_date: value.start_date, + end_date: value.end_date, + metadata: value.metadata, + } + } +} + +impl From for MandateAmountData { + fn from(value: diesel_models::enums::MandateAmountData) -> Self { + Self { + amount: value.amount, + currency: value.currency, + start_date: value.start_date, + end_date: value.end_date, + metadata: value.metadata, + } + } +} + impl From for MandateData { fn from(value: ApiMandateData) -> Self { Self { diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index b9cfba86d41..24a79e2905e 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1,35 +1,49 @@ use api_models::enums::Connector; use common_enums as storage_enums; use common_utils::{ - encryption::Encryption, errors::{CustomResult, ValidationError}, - id_type, pii, type_name, + id_type, pii, types::{ keymanager::{self, KeyManagerState}, MinorUnit, }, }; +use diesel_models::{ + PaymentAttempt as DieselPaymentAttempt, PaymentAttemptNew as DieselPaymentAttemptNew, +}; use error_stack::ResultExt; -use masking::PeekInterface; +use masking::Secret; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use super::PaymentIntent; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use crate::merchant_key_store::MerchantKeyStore; use crate::{ behaviour, errors, mandates::{MandateDataType, MandateDetails}, - type_encryption::{crypto_operation, AsyncLift, CryptoOperation}, - ForeignIDRef, RemoteStorageObject, + ForeignIDRef, }; #[async_trait::async_trait] pub trait PaymentAttemptInterface { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn insert_payment_attempt( &self, payment_attempt: PaymentAttemptNew, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn insert_payment_attempt( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + payment_attempt: PaymentAttempt, + storage_scheme: storage_enums::MerchantStorageScheme, + ) -> error_stack::Result; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn update_payment_attempt_with_attempt_id( &self, this: PaymentAttempt, @@ -37,6 +51,17 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn update_payment_attempt_with_attempt_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + this: PaymentAttempt, + payment_attempt: PaymentAttemptUpdate, + storage_scheme: storage_enums::MerchantStorageScheme, + ) -> error_stack::Result; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id( &self, connector_transaction_id: &str, @@ -45,6 +70,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id( &self, payment_id: &id_type::PaymentId, @@ -52,6 +78,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id( &self, payment_id: &id_type::PaymentId, @@ -59,6 +86,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_merchant_id_connector_txn_id( &self, merchant_id: &id_type::MerchantId, @@ -66,6 +94,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id( &self, payment_id: &id_type::PaymentId, @@ -74,6 +103,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_attempt_id_merchant_id( &self, attempt_id: &str, @@ -81,6 +111,17 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn find_payment_attempt_by_attempt_id_merchant_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + attempt_id: &str, + merchant_id: &id_type::MerchantId, + storage_scheme: storage_enums::MerchantStorageScheme, + ) -> error_stack::Result; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_preprocessing_id_merchant_id( &self, preprocessing_id: &str, @@ -88,6 +129,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_attempts_by_merchant_id_payment_id( &self, merchant_id: &id_type::MerchantId, @@ -95,6 +137,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn get_filters_for_payments( &self, pi: &[PaymentIntent], @@ -102,6 +145,7 @@ pub trait PaymentAttemptInterface { storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[allow(clippy::too_many_arguments)] async fn get_total_count_of_filtered_payment_attempts( &self, @@ -490,434 +534,1634 @@ pub enum PaymentAttemptUpdate { }, } +#[cfg(all(feature = "v2", feature = "payment_v2"))] +impl From for diesel_models::PaymentAttemptUpdateInternal { + fn from(payment_attempt_update: PaymentAttemptUpdate) -> Self { + match payment_attempt_update { + PaymentAttemptUpdate::Update { + amount, + currency, + status, + authentication_type, + payment_method, + payment_token, + payment_method_data, + payment_method_type, + payment_experience, + business_sub_label, + amount_to_capture, + capture_method, + surcharge_amount, + tax_amount, + fingerprint_id, + updated_by, + payment_method_billing_address_id, + } => Self { + amount: Some(amount), + currency: Some(currency), + status: Some(status), + authentication_type, + payment_method, + payment_token, + modified_at: common_utils::date_time::now(), + payment_method_data, + payment_method_type, + payment_experience, + business_sub_label, + amount_to_capture, + capture_method, + surcharge_amount, + tax_amount, + fingerprint_id, + payment_method_billing_address_id, + updated_by, + net_amount: None, + connector_transaction_id: None, + connector: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + error_code: None, + connector_metadata: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + connector_response_reference_id: None, + multiple_capture_count: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::AuthenticationTypeUpdate { + authentication_type, + updated_by, + } => Self { + authentication_type: Some(authentication_type), + modified_at: common_utils::date_time::now(), + updated_by, + amount: None, + net_amount: None, + currency: None, + status: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::ConfirmUpdate { + amount, + currency, + authentication_type, + capture_method, + status, + payment_method, + browser_info, + connector, + payment_token, + payment_method_data, + payment_method_type, + payment_experience, + business_sub_label, + straight_through_algorithm, + error_code, + error_message, + amount_capturable, + updated_by, + merchant_connector_id, + surcharge_amount, + tax_amount, + external_three_ds_authentication_attempted, + authentication_connector, + authentication_id, + payment_method_billing_address_id, + fingerprint_id, + payment_method_id, + client_source, + client_version, + customer_acceptance, + shipping_cost, + order_tax_amount, + } => Self { + amount: Some(amount), + currency: Some(currency), + authentication_type, + status: Some(status), + payment_method, + modified_at: common_utils::date_time::now(), + browser_info, + connector: connector.map(Some), + payment_token, + payment_method_data, + payment_method_type, + payment_experience, + business_sub_label, + straight_through_algorithm, + error_code, + error_message, + amount_capturable, + updated_by, + merchant_connector_id: merchant_connector_id.map(Some), + surcharge_amount, + tax_amount, + external_three_ds_authentication_attempted, + authentication_connector, + authentication_id, + payment_method_billing_address_id, + fingerprint_id, + payment_method_id, + capture_method, + client_source, + client_version, + customer_acceptance, + net_amount: None, + connector_transaction_id: None, + amount_to_capture: None, + cancellation_reason: None, + mandate_id: None, + connector_metadata: None, + preprocessing_step_id: None, + error_reason: None, + connector_response_reference_id: None, + multiple_capture_count: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + charge_id: None, + card_network: None, + shipping_cost, + order_tax_amount, + }, + PaymentAttemptUpdate::VoidUpdate { + status, + cancellation_reason, + updated_by, + } => Self { + status: Some(status), + cancellation_reason, + modified_at: common_utils::date_time::now(), + updated_by, + amount: None, + net_amount: None, + currency: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::RejectUpdate { + status, + error_code, + error_message, + updated_by, + } => Self { + status: Some(status), + modified_at: common_utils::date_time::now(), + error_code, + error_message, + updated_by, + amount: None, + net_amount: None, + currency: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::BlocklistUpdate { + status, + error_code, + error_message, + updated_by, + } => Self { + status: Some(status), + modified_at: common_utils::date_time::now(), + error_code, + connector: Some(None), + error_message, + updated_by, + merchant_connector_id: Some(None), + amount: None, + net_amount: None, + currency: None, + connector_transaction_id: None, + amount_to_capture: None, + authentication_type: None, + payment_method: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::PaymentMethodDetailsUpdate { + payment_method_id, + updated_by, + } => Self { + payment_method_id, + modified_at: common_utils::date_time::now(), + updated_by, + amount: None, + net_amount: None, + currency: None, + status: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::ResponseUpdate { + status, + connector, + connector_transaction_id, + authentication_type, + payment_method_id, + mandate_id, + connector_metadata, + payment_token, + error_code, + error_message, + error_reason, + connector_response_reference_id, + amount_capturable, + updated_by, + authentication_data, + encoded_data, + unified_code, + unified_message, + payment_method_data, + charge_id, + } => Self { + status: Some(status), + connector: connector.map(Some), + connector_transaction_id, + authentication_type, + payment_method_id, + modified_at: common_utils::date_time::now(), + mandate_id, + connector_metadata, + error_code, + error_message, + payment_token, + error_reason, + connector_response_reference_id, + amount_capturable, + updated_by, + authentication_data, + encoded_data, + unified_code, + unified_message, + payment_method_data, + charge_id, + amount: None, + net_amount: None, + currency: None, + amount_to_capture: None, + payment_method: None, + cancellation_reason: None, + browser_info: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + capture_method: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + merchant_connector_id: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::ErrorUpdate { + connector, + status, + error_code, + error_message, + error_reason, + amount_capturable, + updated_by, + unified_code, + unified_message, + connector_transaction_id, + payment_method_data, + authentication_type, + } => Self { + connector: connector.map(Some), + status: Some(status), + error_message, + error_code, + modified_at: common_utils::date_time::now(), + error_reason, + amount_capturable, + updated_by, + unified_code, + unified_message, + connector_transaction_id, + payment_method_data, + authentication_type, + amount: None, + net_amount: None, + currency: None, + amount_to_capture: None, + payment_method: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + connector_metadata: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::StatusUpdate { status, updated_by } => Self { + status: Some(status), + modified_at: common_utils::date_time::now(), + updated_by, + amount: None, + net_amount: None, + currency: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::UpdateTrackers { + payment_token, + connector, + straight_through_algorithm, + amount_capturable, + surcharge_amount, + tax_amount, + updated_by, + merchant_connector_id, + } => Self { + payment_token, + modified_at: common_utils::date_time::now(), + connector: connector.map(Some), + straight_through_algorithm, + amount_capturable, + surcharge_amount, + tax_amount, + updated_by, + merchant_connector_id: merchant_connector_id.map(Some), + amount: None, + net_amount: None, + currency: None, + status: None, + connector_transaction_id: None, + amount_to_capture: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::UnresolvedResponseUpdate { + status, + connector, + connector_transaction_id, + payment_method_id, + error_code, + error_message, + error_reason, + connector_response_reference_id, + updated_by, + } => Self { + status: Some(status), + connector: connector.map(Some), + connector_transaction_id, + payment_method_id, + modified_at: common_utils::date_time::now(), + error_code, + error_message, + error_reason, + connector_response_reference_id, + updated_by, + amount: None, + net_amount: None, + currency: None, + amount_to_capture: None, + authentication_type: None, + payment_method: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + capture_method: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::PreprocessingUpdate { + status, + payment_method_id, + connector_metadata, + preprocessing_step_id, + connector_transaction_id, + connector_response_reference_id, + updated_by, + } => Self { + status: Some(status), + payment_method_id, + modified_at: common_utils::date_time::now(), + connector_metadata, + preprocessing_step_id, + connector_transaction_id, + connector_response_reference_id, + updated_by, + amount: None, + net_amount: None, + currency: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + error_reason: None, + capture_method: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::CaptureUpdate { + multiple_capture_count, + updated_by, + amount_to_capture, + } => Self { + multiple_capture_count, + modified_at: common_utils::date_time::now(), + updated_by, + amount_to_capture, + amount: None, + net_amount: None, + currency: None, + status: None, + connector_transaction_id: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::AmountToCaptureUpdate { + status, + amount_capturable, + updated_by, + } => Self { + status: Some(status), + modified_at: common_utils::date_time::now(), + amount_capturable: Some(amount_capturable), + updated_by, + amount: None, + net_amount: None, + currency: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::ConnectorResponse { + authentication_data, + encoded_data, + connector_transaction_id, + connector, + updated_by, + charge_id, + } => Self { + authentication_data, + encoded_data, + connector_transaction_id, + connector: connector.map(Some), + modified_at: common_utils::date_time::now(), + updated_by, + charge_id, + amount: None, + net_amount: None, + currency: None, + status: None, + amount_to_capture: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::IncrementalAuthorizationAmountUpdate { + amount, + amount_capturable, + } => Self { + amount: Some(amount), + modified_at: common_utils::date_time::now(), + amount_capturable: Some(amount_capturable), + net_amount: None, + currency: None, + status: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + updated_by: String::default(), + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::AuthenticationUpdate { + status, + external_three_ds_authentication_attempted, + authentication_connector, + authentication_id, + updated_by, + } => Self { + status: Some(status), + modified_at: common_utils::date_time::now(), + external_three_ds_authentication_attempted, + authentication_connector, + authentication_id, + updated_by, + amount: None, + net_amount: None, + currency: None, + connector_transaction_id: None, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + error_message: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + error_code: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + error_reason: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + unified_code: None, + unified_message: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + PaymentAttemptUpdate::ManualUpdate { + status, + error_code, + error_message, + error_reason, + updated_by, + unified_code, + unified_message, + connector_transaction_id, + } => Self { + status, + error_code: error_code.map(Some), + modified_at: common_utils::date_time::now(), + error_message: error_message.map(Some), + error_reason: error_reason.map(Some), + updated_by, + unified_code: unified_code.map(Some), + unified_message: unified_message.map(Some), + amount: None, + net_amount: None, + currency: None, + connector_transaction_id, + amount_to_capture: None, + connector: None, + authentication_type: None, + payment_method: None, + payment_method_id: None, + cancellation_reason: None, + mandate_id: None, + browser_info: None, + payment_token: None, + connector_metadata: None, + payment_method_data: None, + payment_method_type: None, + payment_experience: None, + business_sub_label: None, + straight_through_algorithm: None, + preprocessing_step_id: None, + capture_method: None, + connector_response_reference_id: None, + multiple_capture_count: None, + surcharge_amount: None, + tax_amount: None, + amount_capturable: None, + merchant_connector_id: None, + authentication_data: None, + encoded_data: None, + external_three_ds_authentication_attempted: None, + authentication_connector: None, + authentication_id: None, + fingerprint_id: None, + payment_method_billing_address_id: None, + charge_id: None, + client_source: None, + client_version: None, + customer_acceptance: None, + card_network: None, + shipping_cost: None, + order_tax_amount: None, + }, + } + } +} + impl ForeignIDRef for PaymentAttempt { fn foreign_id(&self) -> String { self.attempt_id.clone() } } -use diesel_models::{ - PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, -}; - -#[cfg(all(feature = "v2", feature = "payment_v2"))] +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[async_trait::async_trait] -impl behaviour::Conversion for PaymentIntent { - type DstType = DieselPaymentIntent; - type NewDstType = DieselPaymentIntentNew; +impl behaviour::Conversion for PaymentAttempt { + type DstType = DieselPaymentAttempt; + type NewDstType = DieselPaymentAttemptNew; async fn convert(self) -> CustomResult { - Ok(DieselPaymentIntent { + let card_network = self + .payment_method_data + .as_ref() + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card")) + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card_network")) + .and_then(|network| network.as_str()) + .map(|network| network.to_string()); + Ok(DieselPaymentAttempt { + payment_id: self.payment_id, merchant_id: self.merchant_id, + attempt_id: self.attempt_id, status: self.status, amount: self.amount, currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - statement_descriptor_name: self.statement_descriptor_name, + save_to_locker: self.save_to_locker, + connector: self.connector, + error_message: self.error_message, + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, + payment_method_id: self.payment_method_id, + payment_method: self.payment_method, + connector_transaction_id: self.connector_transaction_id, + capture_method: self.capture_method, + capture_on: self.capture_on, + confirm: self.confirm, + authentication_type: self.authentication_type, created_at: self.created_at, modified_at: self.modified_at, last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, + cancellation_reason: self.cancellation_reason, + amount_to_capture: self.amount_to_capture, + mandate_id: self.mandate_id, + browser_info: self.browser_info, + error_code: self.error_code, + payment_token: self.payment_token, connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - frm_merchant_decision: self.frm_merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, + payment_experience: self.payment_experience, + payment_method_type: self.payment_method_type, + payment_method_data: self.payment_method_data, + business_sub_label: self.business_sub_label, + straight_through_algorithm: self.straight_through_algorithm, + preprocessing_step_id: self.preprocessing_step_id, + mandate_details: self.mandate_details.map(Into::into), + error_reason: self.error_reason, + multiple_capture_count: self.multiple_capture_count, + connector_response_reference_id: self.connector_response_reference_id, + amount_capturable: self.amount_capturable, updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - authorization_count: self.authorization_count, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - frm_metadata: self.frm_metadata, - customer_details: self.customer_details.map(Encryption::from), - billing_address: self.billing_address.map(Encryption::from), - merchant_order_reference_id: self.merchant_order_reference_id, - shipping_address: self.shipping_address.map(Encryption::from), - is_payment_processor_token_flow: self.is_payment_processor_token_flow, - capture_method: self.capture_method, - id: self.id, - authentication_type: self.authentication_type, - amount_to_capture: self.amount_to_capture, - prerouting_algorithm: self.prerouting_algorithm, - merchant_reference_id: self.merchant_reference_id, - surcharge_amount: self.surcharge_amount, - tax_on_surcharge: self.tax_on_surcharge, + merchant_connector_id: self.merchant_connector_id, + authentication_data: self.authentication_data, + encoded_data: self.encoded_data, + unified_code: self.unified_code, + unified_message: self.unified_message, + net_amount: Some(self.net_amount), + external_three_ds_authentication_attempted: self + .external_three_ds_authentication_attempted, + authentication_connector: self.authentication_connector, + authentication_id: self.authentication_id, + mandate_data: self.mandate_data.map(Into::into), + fingerprint_id: self.fingerprint_id, + payment_method_billing_address_id: self.payment_method_billing_address_id, + charge_id: self.charge_id, + client_source: self.client_source, + client_version: self.client_version, + customer_acceptance: self.customer_acceptance, + profile_id: self.profile_id, organization_id: self.organization_id, + card_network, + order_tax_amount: self.order_tax_amount, shipping_cost: self.shipping_cost, - tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, }) } + async fn convert_back( - state: &KeyManagerState, + _state: &KeyManagerState, storage_model: Self::DstType, - key: &masking::Secret>, - key_manager_identifier: keymanager::Identifier, + _key: &Secret>, + _key_manager_identifier: keymanager::Identifier, ) -> CustomResult where Self: Sized, { async { - let inner_decrypt = |inner| async { - crypto_operation( - state, - type_name!(Self::DstType), - CryptoOperation::DecryptOptional(inner), - key_manager_identifier.clone(), - key.peek(), - ) - .await - .and_then(|val| val.try_into_optionaloperation()) - }; + let net_amount = storage_model.get_or_calculate_net_amount(); Ok::>(Self { + payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, + attempt_id: storage_model.attempt_id, status: storage_model.status, amount: storage_model.amount, + net_amount, currency: storage_model.currency, - amount_captured: storage_model.amount_captured, - customer_id: storage_model.customer_id, - description: storage_model.description, - return_url: storage_model.return_url, - metadata: storage_model.metadata, - statement_descriptor_name: storage_model.statement_descriptor_name, + save_to_locker: storage_model.save_to_locker, + connector: storage_model.connector, + error_message: storage_model.error_message, + offer_amount: storage_model.offer_amount, + surcharge_amount: storage_model.surcharge_amount, + tax_amount: storage_model.tax_amount, + payment_method_id: storage_model.payment_method_id, + payment_method: storage_model.payment_method, + connector_transaction_id: storage_model.connector_transaction_id, + capture_method: storage_model.capture_method, + capture_on: storage_model.capture_on, + confirm: storage_model.confirm, + authentication_type: storage_model.authentication_type, created_at: storage_model.created_at, modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, - setup_future_usage: storage_model.setup_future_usage, - off_session: storage_model.off_session, - client_secret: storage_model.client_secret, - active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), - order_details: storage_model.order_details, - allowed_payment_method_types: storage_model.allowed_payment_method_types, + cancellation_reason: storage_model.cancellation_reason, + amount_to_capture: storage_model.amount_to_capture, + mandate_id: storage_model.mandate_id, + browser_info: storage_model.browser_info, + error_code: storage_model.error_code, + payment_token: storage_model.payment_token, connector_metadata: storage_model.connector_metadata, - feature_metadata: storage_model.feature_metadata, - attempt_count: storage_model.attempt_count, - profile_id: storage_model.profile_id, - frm_merchant_decision: storage_model.frm_merchant_decision, - payment_link_id: storage_model.payment_link_id, - payment_confirm_source: storage_model.payment_confirm_source, + payment_experience: storage_model.payment_experience, + payment_method_type: storage_model.payment_method_type, + payment_method_data: storage_model.payment_method_data, + business_sub_label: storage_model.business_sub_label, + straight_through_algorithm: storage_model.straight_through_algorithm, + preprocessing_step_id: storage_model.preprocessing_step_id, + mandate_details: storage_model.mandate_details.map(Into::into), + error_reason: storage_model.error_reason, + multiple_capture_count: storage_model.multiple_capture_count, + connector_response_reference_id: storage_model.connector_response_reference_id, + amount_capturable: storage_model.amount_capturable, updated_by: storage_model.updated_by, - surcharge_applicable: storage_model.surcharge_applicable, - request_incremental_authorization: storage_model.request_incremental_authorization, - authorization_count: storage_model.authorization_count, - session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: storage_model - .request_external_three_ds_authentication, - charges: storage_model.charges, - frm_metadata: storage_model.frm_metadata, - customer_details: storage_model - .customer_details - .async_lift(inner_decrypt) - .await?, - billing_address: storage_model - .billing_address - .async_lift(inner_decrypt) - .await?, - merchant_order_reference_id: storage_model.merchant_order_reference_id, - shipping_address: storage_model - .shipping_address - .async_lift(inner_decrypt) - .await?, - is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, - capture_method: storage_model.capture_method, - id: storage_model.id, - merchant_reference_id: storage_model.merchant_reference_id, + authentication_data: storage_model.authentication_data, + encoded_data: storage_model.encoded_data, + merchant_connector_id: storage_model.merchant_connector_id, + unified_code: storage_model.unified_code, + unified_message: storage_model.unified_message, + external_three_ds_authentication_attempted: storage_model + .external_three_ds_authentication_attempted, + authentication_connector: storage_model.authentication_connector, + authentication_id: storage_model.authentication_id, + mandate_data: storage_model.mandate_data.map(Into::into), + payment_method_billing_address_id: storage_model.payment_method_billing_address_id, + fingerprint_id: storage_model.fingerprint_id, + charge_id: storage_model.charge_id, + client_source: storage_model.client_source, + client_version: storage_model.client_version, + customer_acceptance: storage_model.customer_acceptance, + profile_id: storage_model.profile_id, organization_id: storage_model.organization_id, - authentication_type: storage_model.authentication_type, - amount_to_capture: storage_model.amount_to_capture, - prerouting_algorithm: storage_model.prerouting_algorithm, - surcharge_amount: storage_model.surcharge_amount, - tax_on_surcharge: storage_model.tax_on_surcharge, + order_tax_amount: storage_model.order_tax_amount, shipping_cost: storage_model.shipping_cost, - tax_details: storage_model.tax_details, - skip_external_tax_calculation: storage_model.skip_external_tax_calculation, }) } .await .change_context(ValidationError::InvalidValue { - message: "Failed while decrypting payment intent".to_string(), + message: "Failed while decrypting payment attempt".to_string(), }) } async fn construct_new(self) -> CustomResult { - Ok(DieselPaymentIntentNew { + let card_network = self + .payment_method_data + .as_ref() + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card")) + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card_network")) + .and_then(|network| network.as_str()) + .map(|network| network.to_string()); + Ok(DieselPaymentAttemptNew { + payment_id: self.payment_id, merchant_id: self.merchant_id, + attempt_id: self.attempt_id, status: self.status, amount: self.amount, currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - statement_descriptor_name: self.statement_descriptor_name, + save_to_locker: self.save_to_locker, + connector: self.connector, + error_message: self.error_message, + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, + payment_method_id: self.payment_method_id, + payment_method: self.payment_method, + capture_method: self.capture_method, + capture_on: self.capture_on, + confirm: self.confirm, + authentication_type: self.authentication_type, created_at: self.created_at, modified_at: self.modified_at, last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, + cancellation_reason: self.cancellation_reason, + amount_to_capture: self.amount_to_capture, + mandate_id: self.mandate_id, + browser_info: self.browser_info, + payment_token: self.payment_token, + error_code: self.error_code, connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - frm_merchant_decision: self.frm_merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, + payment_experience: self.payment_experience, + payment_method_type: self.payment_method_type, + payment_method_data: self.payment_method_data, + business_sub_label: self.business_sub_label, + straight_through_algorithm: self.straight_through_algorithm, + preprocessing_step_id: self.preprocessing_step_id, + mandate_details: self.mandate_details.map(Into::into), + error_reason: self.error_reason, + connector_response_reference_id: self.connector_response_reference_id, + multiple_capture_count: self.multiple_capture_count, + amount_capturable: self.amount_capturable, updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - authorization_count: self.authorization_count, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - frm_metadata: self.frm_metadata, - customer_details: self.customer_details.map(Encryption::from), - billing_address: self.billing_address.map(Encryption::from), - merchant_order_reference_id: self.merchant_order_reference_id, - shipping_address: self.shipping_address.map(Encryption::from), - is_payment_processor_token_flow: self.is_payment_processor_token_flow, - capture_method: self.capture_method, - id: self.id, - merchant_reference_id: self.merchant_reference_id, - authentication_type: self.authentication_type, - amount_to_capture: self.amount_to_capture, - prerouting_algorithm: self.prerouting_algorithm, - surcharge_amount: self.surcharge_amount, - tax_on_surcharge: self.tax_on_surcharge, + merchant_connector_id: self.merchant_connector_id, + authentication_data: self.authentication_data, + encoded_data: self.encoded_data, + unified_code: self.unified_code, + unified_message: self.unified_message, + net_amount: Some(self.net_amount), + external_three_ds_authentication_attempted: self + .external_three_ds_authentication_attempted, + authentication_connector: self.authentication_connector, + authentication_id: self.authentication_id, + mandate_data: self.mandate_data.map(Into::into), + fingerprint_id: self.fingerprint_id, + payment_method_billing_address_id: self.payment_method_billing_address_id, + charge_id: self.charge_id, + client_source: self.client_source, + client_version: self.client_version, + customer_acceptance: self.customer_acceptance, + profile_id: self.profile_id, organization_id: self.organization_id, + card_network, + order_tax_amount: self.order_tax_amount, shipping_cost: self.shipping_cost, - tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, }) } } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] +#[cfg(all(feature = "v2", feature = "payment_v2"))] #[async_trait::async_trait] -impl behaviour::Conversion for PaymentIntent { - type DstType = DieselPaymentIntent; - type NewDstType = DieselPaymentIntentNew; +impl behaviour::Conversion for PaymentAttempt { + type DstType = DieselPaymentAttempt; + type NewDstType = DieselPaymentAttemptNew; async fn convert(self) -> CustomResult { - Ok(DieselPaymentIntent { + let card_network = self + .payment_method_data + .as_ref() + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card")) + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card_network")) + .and_then(|network| network.as_str()) + .map(|network| network.to_string()); + Ok(DieselPaymentAttempt { payment_id: self.payment_id, merchant_id: self.merchant_id, + attempt_id: self.attempt_id, status: self.status, amount: self.amount, currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - connector_id: self.connector_id, - shipping_address_id: self.shipping_address_id, - billing_address_id: self.billing_address_id, - statement_descriptor_name: self.statement_descriptor_name, - statement_descriptor_suffix: self.statement_descriptor_suffix, + save_to_locker: self.save_to_locker, + connector: self.connector, + error_message: self.error_message, + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, + payment_method_id: self.payment_method_id, + payment_method: self.payment_method, + connector_transaction_id: self.connector_transaction_id, + capture_method: self.capture_method, + capture_on: self.capture_on, + confirm: self.confirm, + authentication_type: self.authentication_type, created_at: self.created_at, modified_at: self.modified_at, last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - business_country: self.business_country, - business_label: self.business_label, - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, + cancellation_reason: self.cancellation_reason, + amount_to_capture: self.amount_to_capture, + mandate_id: self.mandate_id, + browser_info: self.browser_info, + error_code: self.error_code, + payment_token: self.payment_token, connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - merchant_decision: self.merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, + payment_experience: self.payment_experience, + payment_method_type: self.payment_method_type, + payment_method_data: self.payment_method_data, + business_sub_label: self.business_sub_label, + straight_through_algorithm: self.straight_through_algorithm, + preprocessing_step_id: self.preprocessing_step_id, + mandate_details: self.mandate_details.map(Into::into), + error_reason: self.error_reason, + multiple_capture_count: self.multiple_capture_count, + connector_response_reference_id: self.connector_response_reference_id, + amount_capturable: self.amount_capturable, updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - incremental_authorization_allowed: self.incremental_authorization_allowed, - authorization_count: self.authorization_count, + merchant_connector_id: self.merchant_connector_id, + authentication_data: self.authentication_data, + encoded_data: self.encoded_data, + unified_code: self.unified_code, + unified_message: self.unified_message, + net_amount: Some(self.net_amount), + external_three_ds_authentication_attempted: self + .external_three_ds_authentication_attempted, + authentication_connector: self.authentication_connector, + authentication_id: self.authentication_id, + mandate_data: self.mandate_data.map(Into::into), fingerprint_id: self.fingerprint_id, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - frm_metadata: self.frm_metadata, - customer_details: self.customer_details.map(Encryption::from), - billing_details: self.billing_details.map(Encryption::from), - merchant_order_reference_id: self.merchant_order_reference_id, - shipping_details: self.shipping_details.map(Encryption::from), - is_payment_processor_token_flow: self.is_payment_processor_token_flow, + payment_method_billing_address_id: self.payment_method_billing_address_id, + charge_id: self.charge_id, + client_source: self.client_source, + client_version: self.client_version, + customer_acceptance: self.customer_acceptance, + profile_id: self.profile_id, organization_id: self.organization_id, + card_network, + order_tax_amount: self.order_tax_amount, shipping_cost: self.shipping_cost, - tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, }) } async fn convert_back( - state: &KeyManagerState, + _state: &KeyManagerState, storage_model: Self::DstType, - key: &masking::Secret>, - key_manager_identifier: keymanager::Identifier, + _key: &Secret>, + _key_manager_identifier: keymanager::Identifier, ) -> CustomResult where Self: Sized, { async { - let inner_decrypt = |inner| async { - crypto_operation( - state, - type_name!(Self::DstType), - CryptoOperation::DecryptOptional(inner), - key_manager_identifier.clone(), - key.peek(), - ) - .await - .and_then(|val| val.try_into_optionaloperation()) - }; + let net_amount = storage_model.get_or_calculate_net_amount(); Ok::>(Self { payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, + attempt_id: storage_model.attempt_id, status: storage_model.status, amount: storage_model.amount, + net_amount, currency: storage_model.currency, - amount_captured: storage_model.amount_captured, - customer_id: storage_model.customer_id, - description: storage_model.description, - return_url: storage_model.return_url, - metadata: storage_model.metadata, - connector_id: storage_model.connector_id, - shipping_address_id: storage_model.shipping_address_id, - billing_address_id: storage_model.billing_address_id, - statement_descriptor_name: storage_model.statement_descriptor_name, - statement_descriptor_suffix: storage_model.statement_descriptor_suffix, + save_to_locker: storage_model.save_to_locker, + connector: storage_model.connector, + error_message: storage_model.error_message, + offer_amount: storage_model.offer_amount, + surcharge_amount: storage_model.surcharge_amount, + tax_amount: storage_model.tax_amount, + payment_method_id: storage_model.payment_method_id, + payment_method: storage_model.payment_method, + connector_transaction_id: storage_model.connector_transaction_id, + capture_method: storage_model.capture_method, + capture_on: storage_model.capture_on, + confirm: storage_model.confirm, + authentication_type: storage_model.authentication_type, created_at: storage_model.created_at, modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, - setup_future_usage: storage_model.setup_future_usage, - off_session: storage_model.off_session, - client_secret: storage_model.client_secret, - active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), - business_country: storage_model.business_country, - business_label: storage_model.business_label, - order_details: storage_model.order_details, - allowed_payment_method_types: storage_model.allowed_payment_method_types, + cancellation_reason: storage_model.cancellation_reason, + amount_to_capture: storage_model.amount_to_capture, + mandate_id: storage_model.mandate_id, + browser_info: storage_model.browser_info, + error_code: storage_model.error_code, + payment_token: storage_model.payment_token, connector_metadata: storage_model.connector_metadata, - feature_metadata: storage_model.feature_metadata, - attempt_count: storage_model.attempt_count, - profile_id: storage_model.profile_id, - merchant_decision: storage_model.merchant_decision, - payment_link_id: storage_model.payment_link_id, - payment_confirm_source: storage_model.payment_confirm_source, + payment_experience: storage_model.payment_experience, + payment_method_type: storage_model.payment_method_type, + payment_method_data: storage_model.payment_method_data, + business_sub_label: storage_model.business_sub_label, + straight_through_algorithm: storage_model.straight_through_algorithm, + preprocessing_step_id: storage_model.preprocessing_step_id, + mandate_details: storage_model.mandate_details.map(Into::into), + error_reason: storage_model.error_reason, + multiple_capture_count: storage_model.multiple_capture_count, + connector_response_reference_id: storage_model.connector_response_reference_id, + amount_capturable: storage_model.amount_capturable, updated_by: storage_model.updated_by, - surcharge_applicable: storage_model.surcharge_applicable, - request_incremental_authorization: storage_model.request_incremental_authorization, - incremental_authorization_allowed: storage_model.incremental_authorization_allowed, - authorization_count: storage_model.authorization_count, + authentication_data: storage_model.authentication_data, + encoded_data: storage_model.encoded_data, + merchant_connector_id: storage_model.merchant_connector_id, + unified_code: storage_model.unified_code, + unified_message: storage_model.unified_message, + external_three_ds_authentication_attempted: storage_model + .external_three_ds_authentication_attempted, + authentication_connector: storage_model.authentication_connector, + authentication_id: storage_model.authentication_id, + mandate_data: storage_model.mandate_data.map(Into::into), + payment_method_billing_address_id: storage_model.payment_method_billing_address_id, fingerprint_id: storage_model.fingerprint_id, - session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: storage_model - .request_external_three_ds_authentication, - charges: storage_model.charges, - frm_metadata: storage_model.frm_metadata, - shipping_cost: storage_model.shipping_cost, - tax_details: storage_model.tax_details, - customer_details: storage_model - .customer_details - .async_lift(inner_decrypt) - .await?, - billing_details: storage_model - .billing_details - .async_lift(inner_decrypt) - .await?, - merchant_order_reference_id: storage_model.merchant_order_reference_id, - shipping_details: storage_model - .shipping_details - .async_lift(inner_decrypt) - .await?, - is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, + charge_id: storage_model.charge_id, + client_source: storage_model.client_source, + client_version: storage_model.client_version, + customer_acceptance: storage_model.customer_acceptance, + profile_id: storage_model.profile_id, organization_id: storage_model.organization_id, - skip_external_tax_calculation: storage_model.skip_external_tax_calculation, + order_tax_amount: storage_model.order_tax_amount, + shipping_cost: storage_model.shipping_cost, }) } .await .change_context(ValidationError::InvalidValue { - message: "Failed while decrypting payment intent".to_string(), + message: "Failed while decrypting payment attempt".to_string(), }) } async fn construct_new(self) -> CustomResult { - Ok(DieselPaymentIntentNew { + let card_network = self + .payment_method_data + .as_ref() + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card")) + .and_then(|data| data.as_object()) + .and_then(|card| card.get("card_network")) + .and_then(|network| network.as_str()) + .map(|network| network.to_string()); + Ok(DieselPaymentAttemptNew { payment_id: self.payment_id, merchant_id: self.merchant_id, + attempt_id: self.attempt_id, status: self.status, amount: self.amount, currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - connector_id: self.connector_id, - shipping_address_id: self.shipping_address_id, - billing_address_id: self.billing_address_id, - statement_descriptor_name: self.statement_descriptor_name, - statement_descriptor_suffix: self.statement_descriptor_suffix, + save_to_locker: self.save_to_locker, + connector: self.connector, + error_message: self.error_message, + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, + payment_method_id: self.payment_method_id, + payment_method: self.payment_method, + capture_method: self.capture_method, + capture_on: self.capture_on, + confirm: self.confirm, + authentication_type: self.authentication_type, created_at: self.created_at, modified_at: self.modified_at, last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - business_country: self.business_country, - business_label: self.business_label, - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, + cancellation_reason: self.cancellation_reason, + amount_to_capture: self.amount_to_capture, + mandate_id: self.mandate_id, + browser_info: self.browser_info, + payment_token: self.payment_token, + error_code: self.error_code, connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - merchant_decision: self.merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, + payment_experience: self.payment_experience, + payment_method_type: self.payment_method_type, + payment_method_data: self.payment_method_data, + business_sub_label: self.business_sub_label, + straight_through_algorithm: self.straight_through_algorithm, + preprocessing_step_id: self.preprocessing_step_id, + mandate_details: self.mandate_details.map(Into::into), + error_reason: self.error_reason, + connector_response_reference_id: self.connector_response_reference_id, + multiple_capture_count: self.multiple_capture_count, + amount_capturable: self.amount_capturable, updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - incremental_authorization_allowed: self.incremental_authorization_allowed, - authorization_count: self.authorization_count, + merchant_connector_id: self.merchant_connector_id, + authentication_data: self.authentication_data, + encoded_data: self.encoded_data, + unified_code: self.unified_code, + unified_message: self.unified_message, + net_amount: Some(self.net_amount), + external_three_ds_authentication_attempted: self + .external_three_ds_authentication_attempted, + authentication_connector: self.authentication_connector, + authentication_id: self.authentication_id, + mandate_data: self.mandate_data.map(Into::into), fingerprint_id: self.fingerprint_id, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - frm_metadata: self.frm_metadata, - customer_details: self.customer_details.map(Encryption::from), - billing_details: self.billing_details.map(Encryption::from), - merchant_order_reference_id: self.merchant_order_reference_id, - shipping_details: self.shipping_details.map(Encryption::from), - is_payment_processor_token_flow: self.is_payment_processor_token_flow, + payment_method_billing_address_id: self.payment_method_billing_address_id, + charge_id: self.charge_id, + client_source: self.client_source, + client_version: self.client_version, + customer_acceptance: self.customer_acceptance, + profile_id: self.profile_id, organization_id: self.organization_id, + card_network, + order_tax_amount: self.order_tax_amount, shipping_cost: self.shipping_cost, - tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, }) } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index c4d19c83a39..f5b719fe370 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -3,16 +3,30 @@ use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, crypto::Encryptable, encryption::Encryption, + errors::{CustomResult, ValidationError}, id_type, pii::{self, Email}, - types::{keymanager::KeyManagerState, MinorUnit}, + type_name, + types::{ + keymanager::{self, KeyManagerState}, + MinorUnit, + }, +}; +use diesel_models::{ + PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; -use masking::{Deserialize, Secret}; +use error_stack::ResultExt; +use masking::{Deserialize, PeekInterface, Secret}; use serde::Serialize; use time::PrimitiveDateTime; use super::{payment_attempt::PaymentAttempt, PaymentIntent}; -use crate::{errors, merchant_key_store::MerchantKeyStore, RemoteStorageObject}; +use crate::{ + behaviour, errors, + merchant_key_store::MerchantKeyStore, + type_encryption::{crypto_operation, AsyncLift, CryptoOperation}, + RemoteStorageObject, +}; #[async_trait::async_trait] pub trait PaymentIntentInterface { async fn update_payment_intent( @@ -1563,3 +1577,425 @@ where } } } + +#[cfg(all(feature = "v2", feature = "payment_v2"))] +#[async_trait::async_trait] +impl behaviour::Conversion for PaymentIntent { + type DstType = DieselPaymentIntent; + type NewDstType = DieselPaymentIntentNew; + + async fn convert(self) -> CustomResult { + Ok(DieselPaymentIntent { + merchant_id: self.merchant_id, + status: self.status, + amount: self.amount, + currency: self.currency, + amount_captured: self.amount_captured, + customer_id: self.customer_id, + description: self.description, + return_url: self.return_url, + metadata: self.metadata, + statement_descriptor_name: self.statement_descriptor_name, + created_at: self.created_at, + modified_at: self.modified_at, + last_synced: self.last_synced, + setup_future_usage: self.setup_future_usage, + off_session: self.off_session, + client_secret: self.client_secret, + active_attempt_id: self.active_attempt.get_id(), + order_details: self.order_details, + allowed_payment_method_types: self.allowed_payment_method_types, + connector_metadata: self.connector_metadata, + feature_metadata: self.feature_metadata, + attempt_count: self.attempt_count, + profile_id: self.profile_id, + frm_merchant_decision: self.frm_merchant_decision, + payment_link_id: self.payment_link_id, + payment_confirm_source: self.payment_confirm_source, + updated_by: self.updated_by, + surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, + authorization_count: self.authorization_count, + session_expiry: self.session_expiry, + request_external_three_ds_authentication: self.request_external_three_ds_authentication, + charges: self.charges, + frm_metadata: self.frm_metadata, + customer_details: self.customer_details.map(Encryption::from), + billing_address: self.billing_address.map(Encryption::from), + merchant_order_reference_id: self.merchant_order_reference_id, + shipping_address: self.shipping_address.map(Encryption::from), + is_payment_processor_token_flow: self.is_payment_processor_token_flow, + capture_method: self.capture_method, + id: self.id, + authentication_type: self.authentication_type, + amount_to_capture: self.amount_to_capture, + prerouting_algorithm: self.prerouting_algorithm, + merchant_reference_id: self.merchant_reference_id, + surcharge_amount: self.surcharge_amount, + tax_on_surcharge: self.tax_on_surcharge, + organization_id: self.organization_id, + shipping_cost: self.shipping_cost, + tax_details: self.tax_details, + skip_external_tax_calculation: self.skip_external_tax_calculation, + }) + } + async fn convert_back( + state: &KeyManagerState, + storage_model: Self::DstType, + key: &Secret>, + key_manager_identifier: keymanager::Identifier, + ) -> CustomResult + where + Self: Sized, + { + async { + let inner_decrypt = |inner| async { + crypto_operation( + state, + type_name!(Self::DstType), + CryptoOperation::DecryptOptional(inner), + key_manager_identifier.clone(), + key.peek(), + ) + .await + .and_then(|val| val.try_into_optionaloperation()) + }; + Ok::>(Self { + merchant_id: storage_model.merchant_id, + status: storage_model.status, + amount: storage_model.amount, + currency: storage_model.currency, + amount_captured: storage_model.amount_captured, + customer_id: storage_model.customer_id, + description: storage_model.description, + return_url: storage_model.return_url, + metadata: storage_model.metadata, + statement_descriptor_name: storage_model.statement_descriptor_name, + created_at: storage_model.created_at, + modified_at: storage_model.modified_at, + last_synced: storage_model.last_synced, + setup_future_usage: storage_model.setup_future_usage, + off_session: storage_model.off_session, + client_secret: storage_model.client_secret, + active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), + order_details: storage_model.order_details, + allowed_payment_method_types: storage_model.allowed_payment_method_types, + connector_metadata: storage_model.connector_metadata, + feature_metadata: storage_model.feature_metadata, + attempt_count: storage_model.attempt_count, + profile_id: storage_model.profile_id, + frm_merchant_decision: storage_model.frm_merchant_decision, + payment_link_id: storage_model.payment_link_id, + payment_confirm_source: storage_model.payment_confirm_source, + updated_by: storage_model.updated_by, + surcharge_applicable: storage_model.surcharge_applicable, + request_incremental_authorization: storage_model.request_incremental_authorization, + authorization_count: storage_model.authorization_count, + session_expiry: storage_model.session_expiry, + request_external_three_ds_authentication: storage_model + .request_external_three_ds_authentication, + charges: storage_model.charges, + frm_metadata: storage_model.frm_metadata, + customer_details: storage_model + .customer_details + .async_lift(inner_decrypt) + .await?, + billing_address: storage_model + .billing_address + .async_lift(inner_decrypt) + .await?, + merchant_order_reference_id: storage_model.merchant_order_reference_id, + shipping_address: storage_model + .shipping_address + .async_lift(inner_decrypt) + .await?, + is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, + capture_method: storage_model.capture_method, + id: storage_model.id, + merchant_reference_id: storage_model.merchant_reference_id, + organization_id: storage_model.organization_id, + authentication_type: storage_model.authentication_type, + amount_to_capture: storage_model.amount_to_capture, + prerouting_algorithm: storage_model.prerouting_algorithm, + surcharge_amount: storage_model.surcharge_amount, + tax_on_surcharge: storage_model.tax_on_surcharge, + shipping_cost: storage_model.shipping_cost, + tax_details: storage_model.tax_details, + skip_external_tax_calculation: storage_model.skip_external_tax_calculation, + }) + } + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting payment intent".to_string(), + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(DieselPaymentIntentNew { + merchant_id: self.merchant_id, + status: self.status, + amount: self.amount, + currency: self.currency, + amount_captured: self.amount_captured, + customer_id: self.customer_id, + description: self.description, + return_url: self.return_url, + metadata: self.metadata, + statement_descriptor_name: self.statement_descriptor_name, + created_at: self.created_at, + modified_at: self.modified_at, + last_synced: self.last_synced, + setup_future_usage: self.setup_future_usage, + off_session: self.off_session, + client_secret: self.client_secret, + active_attempt_id: self.active_attempt.get_id(), + order_details: self.order_details, + allowed_payment_method_types: self.allowed_payment_method_types, + connector_metadata: self.connector_metadata, + feature_metadata: self.feature_metadata, + attempt_count: self.attempt_count, + profile_id: self.profile_id, + frm_merchant_decision: self.frm_merchant_decision, + payment_link_id: self.payment_link_id, + payment_confirm_source: self.payment_confirm_source, + updated_by: self.updated_by, + surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, + authorization_count: self.authorization_count, + session_expiry: self.session_expiry, + request_external_three_ds_authentication: self.request_external_three_ds_authentication, + charges: self.charges, + frm_metadata: self.frm_metadata, + customer_details: self.customer_details.map(Encryption::from), + billing_address: self.billing_address.map(Encryption::from), + merchant_order_reference_id: self.merchant_order_reference_id, + shipping_address: self.shipping_address.map(Encryption::from), + is_payment_processor_token_flow: self.is_payment_processor_token_flow, + capture_method: self.capture_method, + id: self.id, + merchant_reference_id: self.merchant_reference_id, + authentication_type: self.authentication_type, + amount_to_capture: self.amount_to_capture, + prerouting_algorithm: self.prerouting_algorithm, + surcharge_amount: self.surcharge_amount, + tax_on_surcharge: self.tax_on_surcharge, + organization_id: self.organization_id, + shipping_cost: self.shipping_cost, + tax_details: self.tax_details, + skip_external_tax_calculation: self.skip_external_tax_calculation, + }) + } +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] +#[async_trait::async_trait] +impl behaviour::Conversion for PaymentIntent { + type DstType = DieselPaymentIntent; + type NewDstType = DieselPaymentIntentNew; + + async fn convert(self) -> CustomResult { + Ok(DieselPaymentIntent { + payment_id: self.payment_id, + merchant_id: self.merchant_id, + status: self.status, + amount: self.amount, + currency: self.currency, + amount_captured: self.amount_captured, + customer_id: self.customer_id, + description: self.description, + return_url: self.return_url, + metadata: self.metadata, + connector_id: self.connector_id, + shipping_address_id: self.shipping_address_id, + billing_address_id: self.billing_address_id, + statement_descriptor_name: self.statement_descriptor_name, + statement_descriptor_suffix: self.statement_descriptor_suffix, + created_at: self.created_at, + modified_at: self.modified_at, + last_synced: self.last_synced, + setup_future_usage: self.setup_future_usage, + off_session: self.off_session, + client_secret: self.client_secret, + active_attempt_id: self.active_attempt.get_id(), + business_country: self.business_country, + business_label: self.business_label, + order_details: self.order_details, + allowed_payment_method_types: self.allowed_payment_method_types, + connector_metadata: self.connector_metadata, + feature_metadata: self.feature_metadata, + attempt_count: self.attempt_count, + profile_id: self.profile_id, + merchant_decision: self.merchant_decision, + payment_link_id: self.payment_link_id, + payment_confirm_source: self.payment_confirm_source, + updated_by: self.updated_by, + surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, + incremental_authorization_allowed: self.incremental_authorization_allowed, + authorization_count: self.authorization_count, + fingerprint_id: self.fingerprint_id, + session_expiry: self.session_expiry, + request_external_three_ds_authentication: self.request_external_three_ds_authentication, + charges: self.charges, + frm_metadata: self.frm_metadata, + customer_details: self.customer_details.map(Encryption::from), + billing_details: self.billing_details.map(Encryption::from), + merchant_order_reference_id: self.merchant_order_reference_id, + shipping_details: self.shipping_details.map(Encryption::from), + is_payment_processor_token_flow: self.is_payment_processor_token_flow, + organization_id: self.organization_id, + shipping_cost: self.shipping_cost, + tax_details: self.tax_details, + skip_external_tax_calculation: self.skip_external_tax_calculation, + }) + } + + async fn convert_back( + state: &KeyManagerState, + storage_model: Self::DstType, + key: &Secret>, + key_manager_identifier: keymanager::Identifier, + ) -> CustomResult + where + Self: Sized, + { + async { + let inner_decrypt = |inner| async { + crypto_operation( + state, + type_name!(Self::DstType), + CryptoOperation::DecryptOptional(inner), + key_manager_identifier.clone(), + key.peek(), + ) + .await + .and_then(|val| val.try_into_optionaloperation()) + }; + Ok::>(Self { + payment_id: storage_model.payment_id, + merchant_id: storage_model.merchant_id, + status: storage_model.status, + amount: storage_model.amount, + currency: storage_model.currency, + amount_captured: storage_model.amount_captured, + customer_id: storage_model.customer_id, + description: storage_model.description, + return_url: storage_model.return_url, + metadata: storage_model.metadata, + connector_id: storage_model.connector_id, + shipping_address_id: storage_model.shipping_address_id, + billing_address_id: storage_model.billing_address_id, + statement_descriptor_name: storage_model.statement_descriptor_name, + statement_descriptor_suffix: storage_model.statement_descriptor_suffix, + created_at: storage_model.created_at, + modified_at: storage_model.modified_at, + last_synced: storage_model.last_synced, + setup_future_usage: storage_model.setup_future_usage, + off_session: storage_model.off_session, + client_secret: storage_model.client_secret, + active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), + business_country: storage_model.business_country, + business_label: storage_model.business_label, + order_details: storage_model.order_details, + allowed_payment_method_types: storage_model.allowed_payment_method_types, + connector_metadata: storage_model.connector_metadata, + feature_metadata: storage_model.feature_metadata, + attempt_count: storage_model.attempt_count, + profile_id: storage_model.profile_id, + merchant_decision: storage_model.merchant_decision, + payment_link_id: storage_model.payment_link_id, + payment_confirm_source: storage_model.payment_confirm_source, + updated_by: storage_model.updated_by, + surcharge_applicable: storage_model.surcharge_applicable, + request_incremental_authorization: storage_model.request_incremental_authorization, + incremental_authorization_allowed: storage_model.incremental_authorization_allowed, + authorization_count: storage_model.authorization_count, + fingerprint_id: storage_model.fingerprint_id, + session_expiry: storage_model.session_expiry, + request_external_three_ds_authentication: storage_model + .request_external_three_ds_authentication, + charges: storage_model.charges, + frm_metadata: storage_model.frm_metadata, + shipping_cost: storage_model.shipping_cost, + tax_details: storage_model.tax_details, + customer_details: storage_model + .customer_details + .async_lift(inner_decrypt) + .await?, + billing_details: storage_model + .billing_details + .async_lift(inner_decrypt) + .await?, + merchant_order_reference_id: storage_model.merchant_order_reference_id, + shipping_details: storage_model + .shipping_details + .async_lift(inner_decrypt) + .await?, + is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, + organization_id: storage_model.organization_id, + skip_external_tax_calculation: storage_model.skip_external_tax_calculation, + }) + } + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting payment intent".to_string(), + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(DieselPaymentIntentNew { + payment_id: self.payment_id, + merchant_id: self.merchant_id, + status: self.status, + amount: self.amount, + currency: self.currency, + amount_captured: self.amount_captured, + customer_id: self.customer_id, + description: self.description, + return_url: self.return_url, + metadata: self.metadata, + connector_id: self.connector_id, + shipping_address_id: self.shipping_address_id, + billing_address_id: self.billing_address_id, + statement_descriptor_name: self.statement_descriptor_name, + statement_descriptor_suffix: self.statement_descriptor_suffix, + created_at: self.created_at, + modified_at: self.modified_at, + last_synced: self.last_synced, + setup_future_usage: self.setup_future_usage, + off_session: self.off_session, + client_secret: self.client_secret, + active_attempt_id: self.active_attempt.get_id(), + business_country: self.business_country, + business_label: self.business_label, + order_details: self.order_details, + allowed_payment_method_types: self.allowed_payment_method_types, + connector_metadata: self.connector_metadata, + feature_metadata: self.feature_metadata, + attempt_count: self.attempt_count, + profile_id: self.profile_id, + merchant_decision: self.merchant_decision, + payment_link_id: self.payment_link_id, + payment_confirm_source: self.payment_confirm_source, + updated_by: self.updated_by, + surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, + incremental_authorization_allowed: self.incremental_authorization_allowed, + authorization_count: self.authorization_count, + fingerprint_id: self.fingerprint_id, + session_expiry: self.session_expiry, + request_external_three_ds_authentication: self.request_external_three_ds_authentication, + charges: self.charges, + frm_metadata: self.frm_metadata, + customer_details: self.customer_details.map(Encryption::from), + billing_details: self.billing_details.map(Encryption::from), + merchant_order_reference_id: self.merchant_order_reference_id, + shipping_details: self.shipping_details.map(Encryption::from), + is_payment_processor_token_flow: self.is_payment_processor_token_flow, + organization_id: self.organization_id, + shipping_cost: self.shipping_cost, + tax_details: self.tax_details, + skip_external_tax_calculation: self.skip_external_tax_calculation, + }) + } +} diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index e4eb60982d4..a325f41404d 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -578,7 +578,7 @@ impl CustomerDeleteBridge for customers::GlobalId { key_manager_state, &self.id, merchant_account.get_id(), - &key_store, + key_store, merchant_account.storage_scheme, ) .await @@ -602,13 +602,9 @@ impl CustomerDeleteBridge for customers::GlobalId { Ok(customer_payment_methods) => { for pm in customer_payment_methods.into_iter() { if pm.payment_method == Some(enums::PaymentMethod::Card) { - cards::delete_card_by_locker_id( - &state, - &self.id, - merchant_account.get_id(), - ) - .await - .switch()?; + cards::delete_card_by_locker_id(state, &self.id, merchant_account.get_id()) + .await + .switch()?; } // No solution as of now, need to discuss this further with payment_method_v2 @@ -676,7 +672,7 @@ impl CustomerDeleteBridge for customers::GlobalId { customer_orig, merchant_account.get_id(), updated_customer, - &key_store, + key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 90c36ba9aa8..7e184b318da 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -786,6 +786,7 @@ pub async fn frm_fulfillment_core( } } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] pub async fn make_fulfillment_api_call( db: &dyn StorageInterface, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index bfb54c8f4e2..1a8bc1b5540 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -405,6 +405,7 @@ where frm_router_data: FrmRouterData, ) -> RouterResult { let db = &*state.store; + let key_manager_state = &state.into(); let frm_check_update = match frm_router_data.response { FrmResponse::Sale(response) => match response { Err(err) => Some(FraudCheckUpdate::ErrorUpdate { @@ -569,15 +570,30 @@ where ), }; + let payment_attempt_update = PaymentAttemptUpdate::RejectUpdate { + status: payment_attempt_status, + error_code: Some(Some(frm_data.fraud_check.frm_status.to_string())), + error_message, + updated_by: frm_data.merchant_account.storage_scheme.to_string(), + }; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] let payment_attempt = db .update_payment_attempt_with_attempt_id( payment_data.get_payment_attempt().clone(), - PaymentAttemptUpdate::RejectUpdate { - status: payment_attempt_status, - error_code: Some(Some(frm_data.fraud_check.frm_status.to_string())), - error_message, - updated_by: frm_data.merchant_account.storage_scheme.to_string(), - }, + payment_attempt_update, + frm_data.merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + let payment_attempt = db + .update_payment_attempt_with_attempt_id( + key_manager_state, + key_store, + payment_data.get_payment_attempt().clone(), + payment_attempt_update, frm_data.merchant_account.storage_scheme, ) .await @@ -587,7 +603,7 @@ where let payment_intent = db .update_payment_intent( - &state.into(), + key_manager_state, payment_data.get_payment_intent().clone(), PaymentIntentUpdate::RejectUpdate { status: payment_intent_status, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 1b23dcab199..22350f77e69 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2046,7 +2046,7 @@ pub async fn delete_card_from_locker( #[cfg(all(feature = "v2", feature = "customer_v2"))] pub async fn delete_card_by_locker_id( state: &routes::SessionState, - id: &String, + id: &str, merchant_id: &id_type::MerchantId, ) -> errors::RouterResult { todo!() @@ -2472,7 +2472,7 @@ pub async fn delete_card_from_hs_locker<'a>( #[instrument(skip_all)] pub async fn delete_card_from_hs_locker_by_global_id<'a>( state: &routes::SessionState, - id: &String, + id: &str, merchant_id: &id_type::MerchantId, card_reference: &'a str, ) -> errors::RouterResult { @@ -4706,6 +4706,7 @@ async fn get_pm_list_context( Ok(payment_method_retrieval_context) } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn perform_surcharge_ops( payment_intent: Option, state: &routes::SessionState, @@ -4750,6 +4751,18 @@ async fn perform_surcharge_ops( Ok(()) } +#[cfg(all(feature = "v2", feature = "payment_v2"))] +async fn perform_surcharge_ops( + _payment_intent: Option, + _state: &routes::SessionState, + _merchant_account: domain::MerchantAccount, + _key_store: domain::MerchantKeyStore, + _business_profile: Option, + _response: &mut api::CustomerPaymentMethodsListResponse, +) -> Result<(), error_stack::Report> { + todo!() +} + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 2af3c52d758..a1d92d8a992 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3757,6 +3757,7 @@ pub enum AttemptType { } impl AttemptType { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] // The function creates a new payment_attempt from the previous payment attempt but doesn't populate fields like payment_method, error_code etc. // Logic to override the fields with data provided in the request should be done after this if required. // In case if fields are not overridden by the request then they contain the same data that was in the previous attempt provided it is populated in this function. @@ -3851,6 +3852,20 @@ impl AttemptType { } } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + // The function creates a new payment_attempt from the previous payment attempt but doesn't populate fields like payment_method, error_code etc. + // Logic to override the fields with data provided in the request should be done after this if required. + // In case if fields are not overridden by the request then they contain the same data that was in the previous attempt provided it is populated in this function. + #[inline(always)] + fn make_new_payment_attempt( + _payment_method_data: Option<&api_models::payments::PaymentMethodData>, + _old_payment_attempt: PaymentAttempt, + _new_attempt_count: i16, + _storage_scheme: enums::MerchantStorageScheme, + ) -> PaymentAttempt { + todo!() + } + #[instrument(skip_all)] pub async fn modify_payment_intent_and_payment_attempt( &self, @@ -3865,19 +3880,34 @@ impl AttemptType { Self::SameOld => Ok((fetched_payment_intent, fetched_payment_attempt)), Self::New => { let db = &*state.store; + let key_manager_state = &state.into(); let new_attempt_count = fetched_payment_intent.attempt_count + 1; + let new_payment_attempt_to_insert = Self::make_new_payment_attempt( + request + .payment_method_data + .as_ref() + .and_then(|request_payment_method_data| { + request_payment_method_data.payment_method_data.as_ref() + }), + fetched_payment_attempt, + new_attempt_count, + storage_scheme, + ); + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] + let new_payment_attempt = db + .insert_payment_attempt(new_payment_attempt_to_insert, storage_scheme) + .await + .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { + payment_id: fetched_payment_intent.get_id().to_owned(), + })?; + + #[cfg(all(feature = "v2", feature = "payment_v2"))] let new_payment_attempt = db .insert_payment_attempt( - Self::make_new_payment_attempt( - request.payment_method_data.as_ref().and_then( - |request_payment_method_data| { - request_payment_method_data.payment_method_data.as_ref() - }, - ), - fetched_payment_attempt, - new_attempt_count, - storage_scheme, - ), + key_manager_state, + key_store, + new_payment_attempt_to_insert, storage_scheme, ) .await @@ -3887,7 +3917,7 @@ impl AttemptType { let updated_payment_intent = db .update_payment_intent( - &state.into(), + key_manager_state, fetched_payment_intent, storage::PaymentIntentUpdate::StatusAndAttemptUpdate { status: payment_intent_status_fsm( diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index fb6bb044778..f397ecd8a2a 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -77,7 +77,6 @@ impl GetTracker, api::PaymentsRequest> for Pa let ephemeral_key = Self::get_ephemeral_key(request, state, merchant_account).await; let merchant_id = merchant_account.get_id(); let storage_scheme = merchant_account.storage_scheme; - let (payment_intent, payment_attempt); let money @ (amount, currency) = payments_create_request_validation(request)?; @@ -322,9 +321,9 @@ impl GetTracker, api::PaymentsRequest> for Pa ) .await?; - payment_intent = db + let payment_intent = db .insert_payment_intent( - &state.into(), + key_manager_state, payment_intent_new, merchant_key_store, storage_scheme, @@ -342,12 +341,27 @@ impl GetTracker, api::PaymentsRequest> for Pa )?; } - payment_attempt = db + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] + let payment_attempt = db .insert_payment_attempt(payment_attempt_new, storage_scheme) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { payment_id: payment_id.clone(), })?; + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + let payment_attempt = db + .insert_payment_attempt( + key_manager_state, + merchant_key_store, + payment_attempt_new, + storage_scheme, + ) + .await + .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { + payment_id: payment_id.clone(), + })?; + let mandate_details_present = payment_attempt.mandate_details.is_some(); helpers::validate_mandate_data_and_future_usage( diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 24ee0697ac3..fb09b4d8b60 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -246,6 +246,11 @@ impl PostUpdateTracker, types::PaymentsAuthor payment_method_id, updated_by: storage_scheme.clone().to_string(), }; + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_v2") + ))] let respond = state .store .update_payment_attempt_with_attempt_id( @@ -254,6 +259,19 @@ impl PostUpdateTracker, types::PaymentsAuthor storage_scheme, ) .await; + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + let respond = state + .store + .update_payment_attempt_with_attempt_id( + &(&state).into(), + &key_store, + payment_attempt, + payment_attempt_update, + storage_scheme, + ) + .await; + if let Err(err) = respond { logger::error!("Error updating payment attempt: {:?}", err); }; @@ -325,15 +343,33 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu }; //payment_attempt update if let Some(payment_attempt_update) = option_payment_attempt_update { - payment_data.payment_attempt = state - .store - .update_payment_attempt_with_attempt_id( - payment_data.payment_attempt.clone(), - payment_attempt_update, - storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] + { + payment_data.payment_attempt = state + .store + .update_payment_attempt_with_attempt_id( + payment_data.payment_attempt.clone(), + payment_attempt_update, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + } + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + { + payment_data.payment_attempt = state + .store + .update_payment_attempt_with_attempt_id( + &state.into(), + key_store, + payment_data.payment_attempt.clone(), + payment_attempt_update, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + } } // payment_intent update if let Some(payment_intent_update) = option_payment_intent_update { diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 75c0fb6d77f..ce2456e08c4 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -360,6 +360,7 @@ where ); let db = &*state.store; + let key_manager_state = &state.into(); let additional_payment_method_data = payments::helpers::update_additional_payment_data_with_connector_response_pm_data( payment_data @@ -389,43 +390,57 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Could not parse the connector response")?; + let payment_attempt_update = storage::PaymentAttemptUpdate::ResponseUpdate { + status: router_data.status, + connector: None, + connector_transaction_id: match resource_id { + types::ResponseId::NoResponseId => None, + types::ResponseId::ConnectorTransactionId(id) + | types::ResponseId::EncodedData(id) => Some(id), + }, + connector_response_reference_id: payment_data + .get_payment_attempt() + .connector_response_reference_id + .clone(), + authentication_type: None, + payment_method_id: payment_data.get_payment_attempt().payment_method_id.clone(), + mandate_id: payment_data + .get_mandate_id() + .and_then(|mandate| mandate.mandate_id.clone()), + connector_metadata, + payment_token: None, + error_code: None, + error_message: None, + error_reason: None, + amount_capturable: if router_data.status.is_terminal_status() { + Some(MinorUnit::new(0)) + } else { + None + }, + updated_by: storage_scheme.to_string(), + authentication_data, + encoded_data, + unified_code: None, + unified_message: None, + payment_method_data: additional_payment_method_data, + charge_id, + }; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] db.update_payment_attempt_with_attempt_id( payment_data.get_payment_attempt().clone(), - storage::PaymentAttemptUpdate::ResponseUpdate { - status: router_data.status, - connector: None, - connector_transaction_id: match resource_id { - types::ResponseId::NoResponseId => None, - types::ResponseId::ConnectorTransactionId(id) - | types::ResponseId::EncodedData(id) => Some(id), - }, - connector_response_reference_id: payment_data - .get_payment_attempt() - .connector_response_reference_id - .clone(), - authentication_type: None, - payment_method_id: payment_data.get_payment_attempt().payment_method_id.clone(), - mandate_id: payment_data - .get_mandate_id() - .and_then(|mandate| mandate.mandate_id.clone()), - connector_metadata, - payment_token: None, - error_code: None, - error_message: None, - error_reason: None, - amount_capturable: if router_data.status.is_terminal_status() { - Some(MinorUnit::new(0)) - } else { - None - }, - updated_by: storage_scheme.to_string(), - authentication_data, - encoded_data, - unified_code: None, - unified_message: None, - payment_method_data: additional_payment_method_data, - charge_id, - }, + payment_attempt_update, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + db.update_payment_attempt_with_attempt_id( + key_manager_state, + key_store, + payment_data.get_payment_attempt().clone(), + payment_attempt_update, storage_scheme, ) .await @@ -445,22 +460,36 @@ where None }; + let payment_attempt_update = storage::PaymentAttemptUpdate::ErrorUpdate { + connector: None, + error_code: Some(Some(error_response.code.clone())), + error_message: Some(Some(error_response.message.clone())), + status: storage_enums::AttemptStatus::Failure, + error_reason: Some(error_response.reason.clone()), + amount_capturable: Some(MinorUnit::new(0)), + updated_by: storage_scheme.to_string(), + unified_code: option_gsm.clone().map(|gsm| gsm.unified_code), + unified_message: option_gsm.map(|gsm| gsm.unified_message), + connector_transaction_id: error_response.connector_transaction_id.clone(), + payment_method_data: additional_payment_method_data, + authentication_type: auth_update, + }; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] db.update_payment_attempt_with_attempt_id( payment_data.get_payment_attempt().clone(), - storage::PaymentAttemptUpdate::ErrorUpdate { - connector: None, - error_code: Some(Some(error_response.code.clone())), - error_message: Some(Some(error_response.message.clone())), - status: storage_enums::AttemptStatus::Failure, - error_reason: Some(error_response.reason.clone()), - amount_capturable: Some(MinorUnit::new(0)), - updated_by: storage_scheme.to_string(), - unified_code: option_gsm.clone().map(|gsm| gsm.unified_code), - unified_message: option_gsm.map(|gsm| gsm.unified_message), - connector_transaction_id: error_response.connector_transaction_id.clone(), - payment_method_data: additional_payment_method_data, - authentication_type: auth_update, - }, + payment_attempt_update, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + db.update_payment_attempt_with_attempt_id( + key_manager_state, + key_store, + payment_data.get_payment_attempt().clone(), + payment_attempt_update, storage_scheme, ) .await @@ -468,6 +497,7 @@ where } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] let payment_attempt = db .insert_payment_attempt(new_payment_attempt, storage_scheme) .await @@ -475,12 +505,25 @@ where payment_id: payment_data.get_payment_intent().get_id().to_owned(), })?; + #[cfg(all(feature = "v2", feature = "payment_v2"))] + let payment_attempt = db + .insert_payment_attempt( + key_manager_state, + key_store, + new_payment_attempt, + storage_scheme, + ) + .await + .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { + payment_id: payment_data.get_payment_intent().get_id().to_owned(), + })?; + // update payment_attempt, connector_response and payment_intent in payment_data payment_data.set_payment_attempt(payment_attempt); let payment_intent = db .update_payment_intent( - &state.into(), + key_manager_state, payment_data.get_payment_intent().clone(), storage::PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { active_attempt_id: payment_data.get_payment_attempt().attempt_id.clone(), @@ -498,6 +541,7 @@ where Ok(()) } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] pub fn make_new_payment_attempt( connector: String, @@ -540,6 +584,9 @@ pub fn make_new_payment_attempt( created_at, modified_at, last_synced, + profile_id: old_payment_attempt.profile_id, + organization_id: old_payment_attempt.organization_id, + shipping_cost: old_payment_attempt.shipping_cost, net_amount: Default::default(), error_message: Default::default(), cancellation_reason: Default::default(), @@ -569,13 +616,21 @@ pub fn make_new_payment_attempt( fingerprint_id: Default::default(), charge_id: Default::default(), customer_acceptance: Default::default(), - profile_id: old_payment_attempt.profile_id, - organization_id: old_payment_attempt.organization_id, - shipping_cost: old_payment_attempt.shipping_cost, - order_tax_amount: None, + order_tax_amount: Default::default(), } } +#[cfg(all(feature = "v2", feature = "payment_v2"))] +#[instrument(skip_all)] +pub fn make_new_payment_attempt( + _connector: String, + _old_payment_attempt: storage::PaymentAttempt, + _new_attempt_count: i16, + _is_step_up: bool, +) -> storage::PaymentAttempt { + todo!() +} + pub async fn config_should_call_gsm( db: &dyn StorageInterface, merchant_id: &common_utils::id_type::MerchantId, diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 7e703967a50..5143068db96 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -1095,7 +1095,7 @@ async fn perform_session_routing_for_pm_type( &session_pm_input.state.clone(), merchant_id, algorithm_id, - &session_pm_input.profile_id, + session_pm_input.profile_id, transaction_type, ) .await?; @@ -1124,7 +1124,7 @@ async fn perform_session_routing_for_pm_type( chosen_connectors, session_pm_input.backend_input.clone(), None, - &session_pm_input.profile_id, + session_pm_input.profile_id, transaction_type, ) .await?; @@ -1140,7 +1140,7 @@ async fn perform_session_routing_for_pm_type( fallback, session_pm_input.backend_input.clone(), None, - &session_pm_input.profile_id, + session_pm_input.profile_id, transaction_type, ) .await?; diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 4892e2552ec..4dc77211857 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -851,7 +851,7 @@ impl MandateInterface for KafkaStore { #[cfg(all(feature = "v2", feature = "customer_v2"))] async fn find_mandate_by_global_id( &self, - id: &String, + id: &str, ) -> CustomResult, errors::StorageError> { self.diesel_store.find_mandate_by_global_id(id).await } @@ -1332,6 +1332,7 @@ impl QueueInterface for KafkaStore { #[async_trait::async_trait] impl PaymentAttemptInterface for KafkaStore { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn insert_payment_attempt( &self, payment_attempt: storage::PaymentAttemptNew, @@ -1353,6 +1354,36 @@ impl PaymentAttemptInterface for KafkaStore { Ok(attempt) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn insert_payment_attempt( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + payment_attempt: storage::PaymentAttempt, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let attempt = self + .diesel_store + .insert_payment_attempt( + key_manager_state, + merchant_key_store, + payment_attempt, + storage_scheme, + ) + .await?; + + if let Err(er) = self + .kafka_producer + .log_payment_attempt(&attempt, None, self.tenant_id.clone()) + .await + { + logger::error!(message="Failed to log analytics event for payment attempt {attempt:?}", error_message=?er) + } + + Ok(attempt) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn update_payment_attempt_with_attempt_id( &self, this: storage::PaymentAttempt, @@ -1375,6 +1406,38 @@ impl PaymentAttemptInterface for KafkaStore { Ok(attempt) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn update_payment_attempt_with_attempt_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + this: storage::PaymentAttempt, + payment_attempt: storage::PaymentAttemptUpdate, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let attempt = self + .diesel_store + .update_payment_attempt_with_attempt_id( + key_manager_state, + merchant_key_store, + this.clone(), + payment_attempt, + storage_scheme, + ) + .await?; + + if let Err(er) = self + .kafka_producer + .log_payment_attempt(&attempt, Some(this), self.tenant_id.clone()) + .await + { + logger::error!(message="Failed to log analytics event for payment attempt {attempt:?}", error_message=?er) + } + + Ok(attempt) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id( &self, connector_transaction_id: &str, @@ -1392,6 +1455,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_merchant_id_connector_txn_id( &self, merchant_id: &id_type::MerchantId, @@ -1407,6 +1471,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id( &self, payment_id: &id_type::PaymentId, @@ -1424,6 +1489,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_attempt_id_merchant_id( &self, attempt_id: &str, @@ -1435,6 +1501,27 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn find_payment_attempt_by_attempt_id_merchant_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + attempt_id: &str, + merchant_id: &id_type::MerchantId, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + self.diesel_store + .find_payment_attempt_by_attempt_id_merchant_id( + key_manager_state, + merchant_key_store, + attempt_id, + merchant_id, + storage_scheme, + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id( &self, payment_id: &id_type::PaymentId, @@ -1450,6 +1537,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id( &self, payment_id: &id_type::PaymentId, @@ -1465,6 +1553,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_preprocessing_id_merchant_id( &self, preprocessing_id: &str, @@ -1480,6 +1569,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn get_filters_for_payments( &self, pi: &[hyperswitch_domain_models::payments::PaymentIntent], @@ -1494,6 +1584,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn get_total_count_of_filtered_payment_attempts( &self, merchant_id: &id_type::MerchantId, @@ -1521,6 +1612,7 @@ impl PaymentAttemptInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_attempts_by_merchant_id_payment_id( &self, merchant_id: &id_type::MerchantId, @@ -1769,7 +1861,7 @@ impl PaymentMethodInterface for KafkaStore { &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - id: &String, + id: &str, limit: Option, ) -> CustomResult, errors::StorageError> { self.diesel_store diff --git a/crates/router/src/db/mandate.rs b/crates/router/src/db/mandate.rs index 24356494471..40e5f88614d 100644 --- a/crates/router/src/db/mandate.rs +++ b/crates/router/src/db/mandate.rs @@ -32,7 +32,7 @@ pub trait MandateInterface { #[cfg(all(feature = "v2", feature = "customer_v2"))] async fn find_mandate_by_global_id( &self, - id: &String, + id: &str, ) -> CustomResult, errors::StorageError>; async fn update_mandate_by_merchant_id_mandate_id( @@ -203,7 +203,7 @@ mod storage { #[instrument(skip_all)] async fn find_mandate_by_global_id( &self, - id: &String, + id: &str, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage_types::Mandate::find_by_global_id(&conn, id) @@ -459,7 +459,7 @@ mod storage { #[instrument(skip_all)] async fn find_mandate_by_global_id( &self, - customer_id: &String, + customer_id: &str, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage_types::Mandate::find_by_global_id(&conn, customer_id) @@ -572,7 +572,7 @@ impl MandateInterface for MockDb { #[cfg(all(feature = "v2", feature = "customer_v2"))] async fn find_mandate_by_global_id( &self, - id: &String, + id: &str, ) -> CustomResult, errors::StorageError> { todo!() } diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index 0541491ab98..cf263def41c 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -53,7 +53,7 @@ pub trait PaymentMethodInterface { &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - id: &String, + id: &str, limit: Option, ) -> CustomResult, errors::StorageError>; @@ -715,7 +715,7 @@ mod storage { &self, _state: &KeyManagerState, _key_store: &domain::MerchantKeyStore, - _id: &String, + _id: &str, _limit: Option, ) -> CustomResult, errors::StorageError> { todo!() @@ -1129,7 +1129,7 @@ mod storage { &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - id: &String, + id: &str, limit: Option, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -1409,7 +1409,7 @@ impl PaymentMethodInterface for MockDb { &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - _id: &String, + _id: &str, _limit: Option, ) -> CustomResult, errors::StorageError> { todo!() diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index d66dc8e51e2..2bafe504dd9 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1576,13 +1576,13 @@ impl BusinessProfile { pub fn server(state: AppState) -> Scope { web::scope("/v2/profiles") .app_data(web::Data::new(state)) - .service(web::resource("").route(web::post().to(super::admin::business_profile_create))) + .service(web::resource("").route(web::post().to(admin::business_profile_create))) .service( web::scope("/{profile_id}") .service( web::resource("") - .route(web::get().to(super::admin::business_profile_retrieve)) - .route(web::put().to(super::admin::business_profile_update)), + .route(web::get().to(admin::business_profile_retrieve)) + .route(web::put().to(admin::business_profile_update)), ) .service( web::resource("/connector_accounts") diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 549c544f30f..e30a6ccc1fd 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -189,7 +189,7 @@ impl ForeignTryFrom for BusinessProfileResponse { let order_fulfillment_time = item .order_fulfillment_time - .map(api_models::admin::OrderFulfillmentTime::new) + .map(api_models::admin::OrderFulfillmentTime::try_new) .transpose() .change_context(errors::ParsingError::IntegerOverflow)?; diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 8d6fb1a8ffb..725cefb8c41 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -401,7 +401,7 @@ impl NewUserMerchant { let merchant_name = if let Some(company_name) = self.company_name.clone() { MerchantName::try_from(company_name) } else { - MerchantName::new("merchant".to_string()) + MerchantName::try_new("merchant".to_string()) .change_context(UserErrors::InternalServerError) .attach_printable("merchant name validation failed") } diff --git a/crates/router/src/types/storage/payment_attempt.rs b/crates/router/src/types/storage/payment_attempt.rs index 782d5ae76a0..18b6e8cc6f9 100644 --- a/crates/router/src/types/storage/payment_attempt.rs +++ b/crates/router/src/types/storage/payment_attempt.rs @@ -85,7 +85,11 @@ impl AttemptStatusExt for enums::AttemptStatus { } #[cfg(test)] -#[cfg(feature = "dummy_connector")] +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_v2"), // Ignoring tests for v2 since they aren't actively running + feature = "dummy_connector" +))] mod tests { #![allow(clippy::expect_used, clippy::unwrap_used, clippy::print_stderr)] use tokio::sync::oneshot; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 46210d4dd09..1442f5b8d48 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -10,7 +10,6 @@ use common_utils::{ ext_traits::{Encode, StringExt, ValueExt}, fp_utils::when, pii, - types::MinorUnit, }; use diesel_models::enums as storage_enums; use error_stack::{report, ResultExt}; @@ -376,7 +375,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { impl ForeignFrom for payments::MandateAmountData { fn foreign_from(from: storage_enums::MandateAmountData) -> Self { Self { - amount: MinorUnit::new(from.amount), + amount: from.amount, currency: from.currency, start_date: from.start_date, end_date: from.end_date, @@ -443,7 +442,7 @@ impl ForeignFrom for hyperswitch_domain_models::mandates: impl ForeignFrom for storage_enums::MandateAmountData { fn foreign_from(from: payments::MandateAmountData) -> Self { Self { - amount: from.amount.get_amount_as_i64(), + amount: from.amount, currency: from.currency, start_date: from.start_date, end_date: from.end_date, diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 12a26cf7c8b..b603b86a72c 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -378,6 +378,9 @@ pub async fn get_mca_from_payment_intent( connector_name: &str, ) -> CustomResult { let db = &*state.store; + let key_manager_state: &KeyManagerState = &state.into(); + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] let payment_attempt = db .find_payment_attempt_by_attempt_id_merchant_id( &payment_intent.active_attempt.get_id(), @@ -386,7 +389,19 @@ pub async fn get_mca_from_payment_intent( ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - let key_manager_state: &KeyManagerState = &state.into(); + + #[cfg(all(feature = "v2", feature = "payment_v2"))] + let payment_attempt = db + .find_payment_attempt_by_attempt_id_merchant_id( + key_manager_state, + key_store, + &payment_intent.active_attempt.get_id(), + merchant_account.get_id(), + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + match payment_attempt.merchant_connector_id { Some(merchant_connector_id) => { #[cfg(feature = "v1")] diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 7147c5071c7..f030df01fc0 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -262,7 +262,7 @@ pub async fn generate_sample_data( true => common_enums::AttemptStatus::Failure, _ => common_enums::AttemptStatus::Charged, }, - amount: amount * 100, + amount: MinorUnit::new(amount * 100), currency: payment_intent.currency, connector: Some( (*connector_vec @@ -289,7 +289,7 @@ pub async fn generate_sample_data( created_at, modified_at, last_synced: Some(last_synced), - amount_to_capture: Some(amount * 100), + amount_to_capture: Some(MinorUnit::new(amount * 100)), connector_response_reference_id: Some(attempt_id.clone()), updated_by: merchant_from_db.storage_scheme.to_string(), save_to_locker: None, @@ -312,7 +312,7 @@ pub async fn generate_sample_data( mandate_details: None, error_reason: None, multiple_capture_count: None, - amount_capturable: i64::default(), + amount_capturable: MinorUnit::new(i64::default()), merchant_connector_id: None, authentication_data: None, encoded_data: None, diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index 6558c14ace3..1a4595bdbbe 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -1,6 +1,10 @@ use api_models::enums::{AuthenticationType, Connector, PaymentMethod, PaymentMethodType}; use common_utils::errors::CustomResult; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use common_utils::types::keymanager::KeyManagerState; use diesel_models::enums as storage_enums; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore; use hyperswitch_domain_models::{ errors::StorageError, payments::payment_attempt::{ @@ -13,6 +17,7 @@ use crate::DataModelExt; #[async_trait::async_trait] impl PaymentAttemptInterface for MockDb { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id( &self, _payment_id: &common_utils::id_type::PaymentId, @@ -24,6 +29,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn get_filters_for_payments( &self, _pi: &[hyperswitch_domain_models::payments::PaymentIntent], @@ -36,6 +42,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn get_total_count_of_filtered_payment_attempts( &self, _merchant_id: &common_utils::id_type::MerchantId, @@ -51,6 +58,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_attempt_id_merchant_id( &self, _attempt_id: &str, @@ -61,6 +69,20 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn find_payment_attempt_by_attempt_id_merchant_id( + &self, + _key_manager_state: &KeyManagerState, + _merchant_key_store: &MerchantKeyStore, + _attempt_id: &str, + _merchant_id: &common_utils::id_type::MerchantId, + _storage_scheme: storage_enums::MerchantStorageScheme, + ) -> CustomResult { + // [#172]: Implement function for `MockDb` + Err(StorageError::MockDbError)? + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_preprocessing_id_merchant_id( &self, _preprocessing_id: &str, @@ -71,6 +93,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_merchant_id_connector_txn_id( &self, _merchant_id: &common_utils::id_type::MerchantId, @@ -81,6 +104,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_attempts_by_merchant_id_payment_id( &self, _merchant_id: &common_utils::id_type::MerchantId, @@ -91,6 +115,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[allow(clippy::panic)] async fn insert_payment_attempt( &self, @@ -168,6 +193,20 @@ impl PaymentAttemptInterface for MockDb { Ok(payment_attempt) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[allow(clippy::panic)] + async fn insert_payment_attempt( + &self, + _key_manager_state: &KeyManagerState, + _merchant_key_store: &MerchantKeyStore, + _payment_attempt: PaymentAttempt, + _storage_scheme: storage_enums::MerchantStorageScheme, + ) -> CustomResult { + // [#172]: Implement function for `MockDb` + Err(StorageError::MockDbError)? + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] // safety: only used for testing #[allow(clippy::unwrap_used)] async fn update_payment_attempt_with_attempt_id( @@ -192,6 +231,20 @@ impl PaymentAttemptInterface for MockDb { Ok(item.clone()) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + async fn update_payment_attempt_with_attempt_id( + &self, + _key_manager_state: &KeyManagerState, + _merchant_key_store: &MerchantKeyStore, + _this: PaymentAttempt, + _payment_attempt: PaymentAttemptUpdate, + _storage_scheme: storage_enums::MerchantStorageScheme, + ) -> CustomResult { + // [#172]: Implement function for `MockDb` + Err(StorageError::MockDbError)? + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id( &self, _connector_transaction_id: &str, @@ -203,6 +256,7 @@ impl PaymentAttemptInterface for MockDb { Err(StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] // safety: only used for testing #[allow(clippy::unwrap_used)] async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id( @@ -222,6 +276,8 @@ impl PaymentAttemptInterface for MockDb { .cloned() .unwrap()) } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[allow(clippy::unwrap_used)] async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id( &self, diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index 85fe2b22d3e..d1df6861657 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -1,5 +1,7 @@ use api_models::enums::{AuthenticationType, Connector, PaymentMethod, PaymentMethodType}; -use common_utils::{errors::CustomResult, fallback_reverse_lookup_not_found, types::MinorUnit}; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use common_utils::types::keymanager::KeyManagerState; +use common_utils::{errors::CustomResult, fallback_reverse_lookup_not_found}; use diesel_models::{ enums::{ MandateAmountData as DieselMandateAmountData, MandateDataType as DieselMandateType, @@ -25,6 +27,10 @@ use hyperswitch_domain_models::{ PaymentIntent, }, }; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use hyperswitch_domain_models::{ + behaviour::ReverseConversion, merchant_key_store::MerchantKeyStore, +}; use redis_interface::HsetnxReply; use router_env::{instrument, tracing}; @@ -39,6 +45,7 @@ use crate::{ #[async_trait::async_trait] impl PaymentAttemptInterface for RouterStore { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn insert_payment_attempt( &self, @@ -57,6 +64,36 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[instrument(skip_all)] + async fn insert_payment_attempt( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + payment_attempt: PaymentAttempt, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let conn = pg_connection_write(self).await?; + payment_attempt + .construct_new() + .await + .change_context(errors::StorageError::EncryptionError)? + .insert(&conn) + .await + .map_err(|error| { + let new_error = diesel_error_to_data_error(error.current_context()); + error.change_context(new_error) + })? + .convert( + key_manager_state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn update_payment_attempt_with_attempt_id( &self, @@ -75,6 +112,40 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[instrument(skip_all)] + async fn update_payment_attempt_with_attempt_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + this: PaymentAttempt, + payment_attempt: PaymentAttemptUpdate, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let conn = pg_connection_write(self).await?; + + Conversion::convert(this) + .await + .change_context(errors::StorageError::EncryptionError)? + .update_with_attempt_id( + &conn, + diesel_models::PaymentAttemptUpdateInternal::from(payment_attempt), + ) + .await + .map_err(|error| { + let new_error = diesel_error_to_data_error(error.current_context()); + error.change_context(new_error) + })? + .convert( + key_manager_state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id( &self, @@ -98,6 +169,7 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id( &self, @@ -119,6 +191,7 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id( &self, @@ -140,6 +213,7 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_merchant_id_connector_txn_id( &self, @@ -161,6 +235,7 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id( &self, @@ -185,6 +260,7 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn get_filters_for_payments( &self, @@ -194,7 +270,7 @@ impl PaymentAttemptInterface for RouterStore { ) -> CustomResult { let conn = pg_connection_read(self).await?; let intents = futures::future::try_join_all(pi.iter().cloned().map(|pi| async { - pi.convert() + Conversion::convert(pi) .await .change_context(errors::StorageError::EncryptionError) })) @@ -225,6 +301,7 @@ impl PaymentAttemptInterface for RouterStore { ) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_preprocessing_id_merchant_id( &self, @@ -247,6 +324,7 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_attempts_by_merchant_id_payment_id( &self, @@ -268,6 +346,7 @@ impl PaymentAttemptInterface for RouterStore { }) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_attempt_id_merchant_id( &self, @@ -286,6 +365,34 @@ impl PaymentAttemptInterface for RouterStore { .map(PaymentAttempt::from_storage_model) } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[instrument(skip_all)] + async fn find_payment_attempt_by_attempt_id_merchant_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + attempt_id: &str, + merchant_id: &common_utils::id_type::MerchantId, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let conn = pg_connection_read(self).await?; + + DieselPaymentAttempt::find_by_merchant_id_attempt_id(&conn, merchant_id, attempt_id) + .await + .map_err(|er| { + let new_err = diesel_error_to_data_error(er.current_context()); + er.change_context(new_err) + })? + .convert( + key_manager_state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn get_total_count_of_filtered_payment_attempts( &self, @@ -332,6 +439,7 @@ impl PaymentAttemptInterface for RouterStore { #[async_trait::async_trait] impl PaymentAttemptInterface for KVRouterStore { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn insert_payment_attempt( &self, @@ -480,6 +588,27 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[instrument(skip_all)] + async fn insert_payment_attempt( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + payment_attempt: PaymentAttempt, + storage_scheme: MerchantStorageScheme, + ) -> error_stack::Result { + // Ignoring storage scheme for v2 implementation + self.router_store + .insert_payment_attempt( + key_manager_state, + merchant_key_store, + payment_attempt, + storage_scheme, + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn update_payment_attempt_with_attempt_id( &self, @@ -603,6 +732,29 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[instrument(skip_all)] + async fn update_payment_attempt_with_attempt_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + this: PaymentAttempt, + payment_attempt: PaymentAttemptUpdate, + storage_scheme: MerchantStorageScheme, + ) -> error_stack::Result { + // Ignoring storage scheme for v2 implementation + self.router_store + .update_payment_attempt_with_attempt_id( + key_manager_state, + merchant_key_store, + this, + payment_attempt, + storage_scheme, + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id( &self, @@ -662,6 +814,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_last_successful_attempt_by_payment_id_merchant_id( &self, @@ -720,6 +873,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_last_successful_or_partially_captured_attempt_by_payment_id_merchant_id( &self, @@ -781,6 +935,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_merchant_id_connector_txn_id( &self, @@ -849,6 +1004,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_payment_id_merchant_id_attempt_id( &self, @@ -902,6 +1058,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_attempt_id_merchant_id( &self, @@ -967,6 +1124,29 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(feature = "v2", feature = "payment_v2"))] + #[instrument(skip_all)] + async fn find_payment_attempt_by_attempt_id_merchant_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &MerchantKeyStore, + attempt_id: &str, + merchant_id: &common_utils::id_type::MerchantId, + storage_scheme: MerchantStorageScheme, + ) -> error_stack::Result { + // Ignoring storage scheme for v2 implementation + self.router_store + .find_payment_attempt_by_attempt_id_merchant_id( + key_manager_state, + merchant_key_store, + attempt_id, + merchant_id, + storage_scheme, + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_attempt_by_preprocessing_id_merchant_id( &self, @@ -1035,6 +1215,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_attempts_by_merchant_id_payment_id( &self, @@ -1084,6 +1265,7 @@ impl PaymentAttemptInterface for KVRouterStore { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn get_filters_for_payments( &self, @@ -1096,6 +1278,7 @@ impl PaymentAttemptInterface for KVRouterStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn get_total_count_of_filtered_payment_attempts( &self, @@ -1130,7 +1313,7 @@ impl DataModelExt for MandateAmountData { fn to_storage_model(self) -> Self::StorageModel { DieselMandateAmountData { - amount: self.amount.get_amount_as_i64(), + amount: self.amount, currency: self.currency, start_date: self.start_date, end_date: self.end_date, @@ -1140,7 +1323,7 @@ impl DataModelExt for MandateAmountData { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - amount: MinorUnit::new(storage_model.amount), + amount: storage_model.amount, currency: storage_model.currency, start_date: storage_model.start_date, end_date: storage_model.end_date, @@ -1198,19 +1381,15 @@ impl DataModelExt for PaymentAttempt { merchant_id: self.merchant_id, attempt_id: self.attempt_id, status: self.status, - amount: self.amount.get_amount_as_i64(), - net_amount: Some(self.net_amount.get_amount_as_i64()), + amount: self.amount, + net_amount: Some(self.net_amount), currency: self.currency, save_to_locker: self.save_to_locker, connector: self.connector, error_message: self.error_message, - offer_amount: self - .offer_amount - .map(|offer_amt| offer_amt.get_amount_as_i64()), - surcharge_amount: self - .surcharge_amount - .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), - tax_amount: self.tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, payment_method_id: self.payment_method_id, payment_method: self.payment_method, connector_transaction_id: self.connector_transaction_id, @@ -1222,9 +1401,7 @@ impl DataModelExt for PaymentAttempt { modified_at: self.modified_at, last_synced: self.last_synced, cancellation_reason: self.cancellation_reason, - amount_to_capture: self - .amount_to_capture - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_to_capture: self.amount_to_capture, mandate_id: self.mandate_id, browser_info: self.browser_info, error_code: self.error_code, @@ -1249,7 +1426,7 @@ impl DataModelExt for PaymentAttempt { error_reason: self.error_reason, multiple_capture_count: self.multiple_capture_count, connector_response_reference_id: self.connector_response_reference_id, - amount_capturable: self.amount_capturable.get_amount_as_i64(), + amount_capturable: self.amount_capturable, updated_by: self.updated_by, authentication_data: self.authentication_data, encoded_data: self.encoded_data, @@ -1276,19 +1453,19 @@ impl DataModelExt for PaymentAttempt { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - net_amount: MinorUnit::new(storage_model.get_or_calculate_net_amount()), + net_amount: storage_model.get_or_calculate_net_amount(), payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, attempt_id: storage_model.attempt_id, status: storage_model.status, - amount: MinorUnit::new(storage_model.amount), + amount: storage_model.amount, currency: storage_model.currency, save_to_locker: storage_model.save_to_locker, connector: storage_model.connector, error_message: storage_model.error_message, - offer_amount: storage_model.offer_amount.map(MinorUnit::new), - surcharge_amount: storage_model.surcharge_amount.map(MinorUnit::new), - tax_amount: storage_model.tax_amount.map(MinorUnit::new), + offer_amount: storage_model.offer_amount, + surcharge_amount: storage_model.surcharge_amount, + tax_amount: storage_model.tax_amount, payment_method_id: storage_model.payment_method_id, payment_method: storage_model.payment_method, connector_transaction_id: storage_model.connector_transaction_id, @@ -1300,7 +1477,7 @@ impl DataModelExt for PaymentAttempt { modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, cancellation_reason: storage_model.cancellation_reason, - amount_to_capture: storage_model.amount_to_capture.map(MinorUnit::new), + amount_to_capture: storage_model.amount_to_capture, mandate_id: storage_model.mandate_id, browser_info: storage_model.browser_info, error_code: storage_model.error_code, @@ -1318,7 +1495,7 @@ impl DataModelExt for PaymentAttempt { error_reason: storage_model.error_reason, multiple_capture_count: storage_model.multiple_capture_count, connector_response_reference_id: storage_model.connector_response_reference_id, - amount_capturable: MinorUnit::new(storage_model.amount_capturable), + amount_capturable: storage_model.amount_capturable, updated_by: storage_model.updated_by, authentication_data: storage_model.authentication_data, encoded_data: storage_model.encoded_data, @@ -1356,19 +1533,15 @@ impl DataModelExt for PaymentAttempt { merchant_id: self.merchant_id, attempt_id: self.attempt_id, status: self.status, - amount: self.amount.get_amount_as_i64(), - net_amount: Some(self.net_amount.get_amount_as_i64()), + amount: self.amount, + net_amount: Some(self.net_amount), currency: self.currency, save_to_locker: self.save_to_locker, connector: self.connector, error_message: self.error_message, - offer_amount: self - .offer_amount - .map(|offer_amt| offer_amt.get_amount_as_i64()), - surcharge_amount: self - .surcharge_amount - .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), - tax_amount: self.tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, payment_method_id: self.payment_method_id, payment_method: self.payment_method, connector_transaction_id: self.connector_transaction_id, @@ -1380,9 +1553,7 @@ impl DataModelExt for PaymentAttempt { modified_at: self.modified_at, last_synced: self.last_synced, cancellation_reason: self.cancellation_reason, - amount_to_capture: self - .amount_to_capture - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_to_capture: self.amount_to_capture, mandate_id: self.mandate_id, browser_info: self.browser_info, error_code: self.error_code, @@ -1407,7 +1578,7 @@ impl DataModelExt for PaymentAttempt { error_reason: self.error_reason, multiple_capture_count: self.multiple_capture_count, connector_response_reference_id: self.connector_response_reference_id, - amount_capturable: self.amount_capturable.get_amount_as_i64(), + amount_capturable: self.amount_capturable, updated_by: self.updated_by, authentication_data: self.authentication_data, encoded_data: self.encoded_data, @@ -1434,19 +1605,19 @@ impl DataModelExt for PaymentAttempt { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - net_amount: MinorUnit::new(storage_model.get_or_calculate_net_amount()), + net_amount: storage_model.get_or_calculate_net_amount(), payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, attempt_id: storage_model.attempt_id, status: storage_model.status, - amount: MinorUnit::new(storage_model.amount), + amount: storage_model.amount, currency: storage_model.currency, save_to_locker: storage_model.save_to_locker, connector: storage_model.connector, error_message: storage_model.error_message, - offer_amount: storage_model.offer_amount.map(MinorUnit::new), - surcharge_amount: storage_model.surcharge_amount.map(MinorUnit::new), - tax_amount: storage_model.tax_amount.map(MinorUnit::new), + offer_amount: storage_model.offer_amount, + surcharge_amount: storage_model.surcharge_amount, + tax_amount: storage_model.tax_amount, payment_method_id: storage_model.payment_method_id, payment_method: storage_model.payment_method, connector_transaction_id: storage_model.connector_transaction_id, @@ -1458,7 +1629,7 @@ impl DataModelExt for PaymentAttempt { modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, cancellation_reason: storage_model.cancellation_reason, - amount_to_capture: storage_model.amount_to_capture.map(MinorUnit::new), + amount_to_capture: storage_model.amount_to_capture, mandate_id: storage_model.mandate_id, browser_info: storage_model.browser_info, error_code: storage_model.error_code, @@ -1476,7 +1647,7 @@ impl DataModelExt for PaymentAttempt { error_reason: storage_model.error_reason, multiple_capture_count: storage_model.multiple_capture_count, connector_response_reference_id: storage_model.connector_response_reference_id, - amount_capturable: MinorUnit::new(storage_model.amount_capturable), + amount_capturable: storage_model.amount_capturable, updated_by: storage_model.updated_by, authentication_data: storage_model.authentication_data, encoded_data: storage_model.encoded_data, @@ -1509,23 +1680,19 @@ impl DataModelExt for PaymentAttemptNew { fn to_storage_model(self) -> Self::StorageModel { DieselPaymentAttemptNew { - net_amount: Some(self.net_amount.get_amount_as_i64()), + net_amount: Some(self.net_amount), payment_id: self.payment_id, merchant_id: self.merchant_id, attempt_id: self.attempt_id, status: self.status, - amount: self.amount.get_amount_as_i64(), + amount: self.amount, currency: self.currency, save_to_locker: self.save_to_locker, connector: self.connector, error_message: self.error_message, - offer_amount: self - .offer_amount - .map(|offer_amt| offer_amt.get_amount_as_i64()), - surcharge_amount: self - .surcharge_amount - .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), - tax_amount: self.tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), + offer_amount: self.offer_amount, + surcharge_amount: self.surcharge_amount, + tax_amount: self.tax_amount, payment_method_id: self.payment_method_id, payment_method: self.payment_method, capture_method: self.capture_method, @@ -1538,9 +1705,7 @@ impl DataModelExt for PaymentAttemptNew { .unwrap_or_else(common_utils::date_time::now), last_synced: self.last_synced, cancellation_reason: self.cancellation_reason, - amount_to_capture: self - .amount_to_capture - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_to_capture: self.amount_to_capture, mandate_id: self.mandate_id, browser_info: self.browser_info, payment_token: self.payment_token, @@ -1565,7 +1730,7 @@ impl DataModelExt for PaymentAttemptNew { error_reason: self.error_reason, connector_response_reference_id: self.connector_response_reference_id, multiple_capture_count: self.multiple_capture_count, - amount_capturable: self.amount_capturable.get_amount_as_i64(), + amount_capturable: self.amount_capturable, updated_by: self.updated_by, authentication_data: self.authentication_data, encoded_data: self.encoded_data, @@ -1592,19 +1757,19 @@ impl DataModelExt for PaymentAttemptNew { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - net_amount: MinorUnit::new(storage_model.get_or_calculate_net_amount()), + net_amount: storage_model.get_or_calculate_net_amount(), payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, attempt_id: storage_model.attempt_id, status: storage_model.status, - amount: MinorUnit::new(storage_model.amount), + amount: storage_model.amount, currency: storage_model.currency, save_to_locker: storage_model.save_to_locker, connector: storage_model.connector, error_message: storage_model.error_message, - offer_amount: storage_model.offer_amount.map(MinorUnit::new), - surcharge_amount: storage_model.surcharge_amount.map(MinorUnit::new), - tax_amount: storage_model.tax_amount.map(MinorUnit::new), + offer_amount: storage_model.offer_amount, + surcharge_amount: storage_model.surcharge_amount, + tax_amount: storage_model.tax_amount, payment_method_id: storage_model.payment_method_id, payment_method: storage_model.payment_method, capture_method: storage_model.capture_method, @@ -1615,7 +1780,7 @@ impl DataModelExt for PaymentAttemptNew { modified_at: Some(storage_model.modified_at), last_synced: storage_model.last_synced, cancellation_reason: storage_model.cancellation_reason, - amount_to_capture: storage_model.amount_to_capture.map(MinorUnit::new), + amount_to_capture: storage_model.amount_to_capture, mandate_id: storage_model.mandate_id, browser_info: storage_model.browser_info, payment_token: storage_model.payment_token, @@ -1633,7 +1798,7 @@ impl DataModelExt for PaymentAttemptNew { error_reason: storage_model.error_reason, connector_response_reference_id: storage_model.connector_response_reference_id, multiple_capture_count: storage_model.multiple_capture_count, - amount_capturable: MinorUnit::new(storage_model.amount_capturable), + amount_capturable: storage_model.amount_capturable, updated_by: storage_model.updated_by, authentication_data: storage_model.authentication_data, encoded_data: storage_model.encoded_data, @@ -1685,7 +1850,7 @@ impl DataModelExt for PaymentAttemptUpdate { payment_method_billing_address_id, updated_by, } => DieselPaymentAttemptUpdate::Update { - amount: amount.get_amount_as_i64(), + amount, currency, status, authentication_type, @@ -1695,12 +1860,10 @@ impl DataModelExt for PaymentAttemptUpdate { payment_method_type, payment_experience, business_sub_label, - amount_to_capture: amount_to_capture - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_to_capture, capture_method, - surcharge_amount: surcharge_amount - .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), - tax_amount: tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), + surcharge_amount, + tax_amount, fingerprint_id, payment_method_billing_address_id, updated_by, @@ -1718,11 +1881,9 @@ impl DataModelExt for PaymentAttemptUpdate { payment_token, connector, straight_through_algorithm, - amount_capturable: amount_capturable - .map(|amount_capturable| amount_capturable.get_amount_as_i64()), - surcharge_amount: surcharge_amount - .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), - tax_amount: tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), + amount_capturable, + surcharge_amount, + tax_amount, updated_by, merchant_connector_id, }, @@ -1785,7 +1946,7 @@ impl DataModelExt for PaymentAttemptUpdate { shipping_cost, order_tax_amount, } => DieselPaymentAttemptUpdate::ConfirmUpdate { - amount: amount.get_amount_as_i64(), + amount, currency, status, authentication_type, @@ -1801,11 +1962,9 @@ impl DataModelExt for PaymentAttemptUpdate { straight_through_algorithm, error_code, error_message, - amount_capturable: amount_capturable - .map(|capture_amt| capture_amt.get_amount_as_i64()), - surcharge_amount: surcharge_amount - .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), - tax_amount: tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), + amount_capturable, + surcharge_amount, + tax_amount, fingerprint_id, updated_by, merchant_connector_id: connector_id, @@ -1863,8 +2022,7 @@ impl DataModelExt for PaymentAttemptUpdate { error_message, error_reason, connector_response_reference_id, - amount_capturable: amount_capturable - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_capturable, updated_by, authentication_data, encoded_data, @@ -1916,8 +2074,7 @@ impl DataModelExt for PaymentAttemptUpdate { error_code, error_message, error_reason, - amount_capturable: amount_capturable - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_capturable, updated_by, unified_code, unified_message, @@ -1932,8 +2089,7 @@ impl DataModelExt for PaymentAttemptUpdate { } => DieselPaymentAttemptUpdate::CaptureUpdate { multiple_capture_count, updated_by, - amount_to_capture: amount_to_capture - .map(|capture_amt| capture_amt.get_amount_as_i64()), + amount_to_capture, }, Self::PreprocessingUpdate { status, @@ -1969,7 +2125,7 @@ impl DataModelExt for PaymentAttemptUpdate { updated_by, } => DieselPaymentAttemptUpdate::AmountToCaptureUpdate { status, - amount_capturable: amount_capturable.get_amount_as_i64(), + amount_capturable, updated_by, }, Self::ConnectorResponse { @@ -1991,8 +2147,8 @@ impl DataModelExt for PaymentAttemptUpdate { amount, amount_capturable, } => DieselPaymentAttemptUpdate::IncrementalAuthorizationAmountUpdate { - amount: amount.get_amount_as_i64(), - amount_capturable: amount_capturable.get_amount_as_i64(), + amount, + amount_capturable, }, Self::AuthenticationUpdate { status, @@ -2050,7 +2206,7 @@ impl DataModelExt for PaymentAttemptUpdate { updated_by, payment_method_billing_address_id, } => Self::Update { - amount: MinorUnit::new(amount), + amount, currency, status, authentication_type, @@ -2060,10 +2216,10 @@ impl DataModelExt for PaymentAttemptUpdate { payment_method_type, payment_experience, business_sub_label, - amount_to_capture: amount_to_capture.map(MinorUnit::new), + amount_to_capture, capture_method, - surcharge_amount: surcharge_amount.map(MinorUnit::new), - tax_amount: tax_amount.map(MinorUnit::new), + surcharge_amount, + tax_amount, fingerprint_id, payment_method_billing_address_id, updated_by, @@ -2081,9 +2237,9 @@ impl DataModelExt for PaymentAttemptUpdate { payment_token, connector, straight_through_algorithm, - amount_capturable: amount_capturable.map(MinorUnit::new), - surcharge_amount: surcharge_amount.map(MinorUnit::new), - tax_amount: tax_amount.map(MinorUnit::new), + amount_capturable, + surcharge_amount, + tax_amount, updated_by, merchant_connector_id: connector_id, }, @@ -2128,7 +2284,7 @@ impl DataModelExt for PaymentAttemptUpdate { shipping_cost, order_tax_amount, } => Self::ConfirmUpdate { - amount: MinorUnit::new(amount), + amount, currency, status, authentication_type, @@ -2144,9 +2300,9 @@ impl DataModelExt for PaymentAttemptUpdate { straight_through_algorithm, error_code, error_message, - amount_capturable: amount_capturable.map(MinorUnit::new), - surcharge_amount: surcharge_amount.map(MinorUnit::new), - tax_amount: tax_amount.map(MinorUnit::new), + amount_capturable, + surcharge_amount, + tax_amount, fingerprint_id, updated_by, merchant_connector_id: connector_id, @@ -2222,7 +2378,7 @@ impl DataModelExt for PaymentAttemptUpdate { error_message, error_reason, connector_response_reference_id, - amount_capturable: amount_capturable.map(MinorUnit::new), + amount_capturable, updated_by, authentication_data, encoded_data, @@ -2274,7 +2430,7 @@ impl DataModelExt for PaymentAttemptUpdate { error_code, error_message, error_reason, - amount_capturable: amount_capturable.map(MinorUnit::new), + amount_capturable, updated_by, unified_code, unified_message, @@ -2287,7 +2443,7 @@ impl DataModelExt for PaymentAttemptUpdate { multiple_capture_count, updated_by, } => Self::CaptureUpdate { - amount_to_capture: amount_to_capture.map(MinorUnit::new), + amount_to_capture, multiple_capture_count, updated_by, }, @@ -2325,7 +2481,7 @@ impl DataModelExt for PaymentAttemptUpdate { updated_by, } => Self::AmountToCaptureUpdate { status, - amount_capturable: MinorUnit::new(amount_capturable), + amount_capturable, updated_by, }, DieselPaymentAttemptUpdate::ConnectorResponse { @@ -2347,8 +2503,8 @@ impl DataModelExt for PaymentAttemptUpdate { amount, amount_capturable, } => Self::IncrementalAuthorizationAmountUpdate { - amount: MinorUnit::new(amount), - amount_capturable: MinorUnit::new(amount_capturable), + amount, + amount_capturable, }, DieselPaymentAttemptUpdate::AuthenticationUpdate { status, From 75400a3af7aaff87e03dbab4e00be8b00ed5a70e Mon Sep 17 00:00:00 2001 From: Kashif Date: Tue, 17 Sep 2024 16:28:58 +0530 Subject: [PATCH 03/48] fix(frm): add feature flag for querying FRM data (#5889) --- .../payments/operations/payment_approve.rs | 20 +++++++++++-------- .../payments/operations/payment_reject.rs | 20 +++++++++++-------- .../payments/operations/payment_status.rs | 20 +++++++++++-------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 38cf72a3e2f..9c5b632eff0 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -136,13 +136,17 @@ impl GetTracker, api::PaymentsCaptureRequest> payment_intent.shipping_address_id = shipping_address.clone().map(|i| i.address_id); payment_intent.billing_address_id = billing_address.clone().map(|i| i.address_id); - let frm_response = db - .find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.get_id().clone()) - .await - .change_context(errors::ApiErrorResponse::PaymentNotFound) - .attach_printable_lazy(|| { - format!("Error while retrieving frm_response, merchant_id: {}, payment_id: {attempt_id}", merchant_account.get_id().get_string_repr()) - }); + let frm_response = if cfg!(feature = "frm") { + db.find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.get_id().clone()) + .await + .change_context(errors::ApiErrorResponse::PaymentNotFound) + .attach_printable_lazy(|| { + format!("Error while retrieving frm_response, merchant_id: {}, payment_id: {attempt_id}", merchant_account.get_id().get_string_repr()) + }) + .ok() + } else { + None + }; let payment_data = PaymentData { flow: PhantomData, @@ -180,7 +184,7 @@ impl GetTracker, api::PaymentsCaptureRequest> multiple_capture_data: None, redirect_response: None, surcharge_details: None, - frm_message: frm_response.ok(), + frm_message: frm_response, payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index 570337a5555..3d67dadd34d 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -117,13 +117,17 @@ impl GetTracker, PaymentsCancelRequest> for P let currency = payment_attempt.currency.get_required_value("currency")?; let amount = payment_attempt.get_total_amount().into(); - let frm_response = db - .find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.get_id().clone()) - .await - .change_context(errors::ApiErrorResponse::PaymentNotFound) - .attach_printable_lazy(|| { - format!("Error while retrieving frm_response, merchant_id: {:?}, payment_id: {attempt_id}", merchant_account.get_id()) - }); + let frm_response = if cfg!(feature = "frm") { + db.find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.get_id().clone()) + .await + .change_context(errors::ApiErrorResponse::PaymentNotFound) + .attach_printable_lazy(|| { + format!("Error while retrieving frm_response, merchant_id: {:?}, payment_id: {attempt_id}", merchant_account.get_id()) + }) + .ok() + } else { + None + }; let profile_id = payment_intent .profile_id @@ -176,7 +180,7 @@ impl GetTracker, PaymentsCancelRequest> for P multiple_capture_data: None, redirect_response: None, surcharge_details: None, - frm_message: frm_response.ok(), + frm_message: frm_response, payment_link_data: None, incremental_authorization_details: None, authorizations: vec![], diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 7c2ef7a10f4..d81258d6671 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -348,13 +348,17 @@ async fn get_tracker_for_sync< format!("Error while retrieving dispute list for, merchant_id: {:?}, payment_id: {payment_id:?}", merchant_account.get_id()) })?; - let frm_response = db - .find_fraud_check_by_payment_id(payment_id.to_owned(), merchant_account.get_id().clone()) - .await - .change_context(errors::ApiErrorResponse::PaymentNotFound) - .attach_printable_lazy(|| { - format!("Error while retrieving frm_response, merchant_id: {:?}, payment_id: {payment_id:?}", merchant_account.get_id()) - }); + let frm_response = if cfg!(feature = "frm") { + db.find_fraud_check_by_payment_id(payment_id.to_owned(), merchant_account.get_id().clone()) + .await + .change_context(errors::ApiErrorResponse::PaymentNotFound) + .attach_printable_lazy(|| { + format!("Error while retrieving frm_response, merchant_id: {:?}, payment_id: {payment_id:?}", merchant_account.get_id()) + }) + .ok() + } else { + None + }; let contains_encoded_data = payment_attempt.encoded_data.is_some(); @@ -472,7 +476,7 @@ async fn get_tracker_for_sync< redirect_response: None, payment_link_data: None, surcharge_details: None, - frm_message: frm_response.ok(), + frm_message: frm_response, incremental_authorization_details: None, authorizations, authentication, From 66de680e6fcaa318694df48dafdaabfceb745961 Mon Sep 17 00:00:00 2001 From: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:29:29 +0530 Subject: [PATCH 04/48] fix(router): cache invalidation in merchant_connector_update flow for v2 (#5918) --- crates/router/src/db/merchant_connector_account.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index 40e53572498..b51e7afb34f 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -785,12 +785,7 @@ impl MerchantConnectorAccountInterface for Store { format!("{}_{}", _profile_id.get_string_repr(), _connector_name).into(), ), cache::CacheKind::Accounts( - format!( - "{}_{}", - _merchant_id.get_string_repr(), - _merchant_connector_id.get_string_repr() - ) - .into(), + format!("{}", _merchant_connector_id.get_string_repr()).into(), ), cache::CacheKind::CGraph( format!( From 97c8e98a48f514b0ba36b109971dd479ae84861e Mon Sep 17 00:00:00 2001 From: AkshayaFoiger <131388445+AkshayaFoiger@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:35:24 +0530 Subject: [PATCH 05/48] fix(router): [stripe] remove passing of customer_acceptance from Mandate Payment Request (#5920) --- .../src/connector/stripe/transformers.rs | 96 ++++++++++--------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 16721994074..910cb94e929 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1786,61 +1786,67 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent _ => payment_data, }; - let customer_acceptance = item + let setup_mandate_details = item .request .setup_mandate_details .as_ref() - .and_then(|mandate_details| mandate_details.customer_acceptance.clone()) - .or(item.request.customer_acceptance.clone()); - - let setup_mandate_details = customer_acceptance - .as_ref() - .map(|customer_acceptance| { - Ok::<_, error_stack::Report>( - match customer_acceptance.acceptance_type { - AcceptanceType::Online => { - let online_mandate = customer_acceptance - .online - .clone() - .get_required_value("online") - .change_context(errors::ConnectorError::MissingRequiredField { - field_name: "online", - })?; - StripeMandateRequest { - mandate_type: StripeMandateType::Online { - ip_address: online_mandate - .ip_address - .get_required_value("ip_address") + .and_then(|mandate_details| { + mandate_details + .customer_acceptance + .as_ref() + .map(|customer_acceptance| { + Ok::<_, error_stack::Report>( + match customer_acceptance.acceptance_type { + AcceptanceType::Online => { + let online_mandate = customer_acceptance + .online + .clone() + .get_required_value("online") .change_context( errors::ConnectorError::MissingRequiredField { - field_name: "ip_address", + field_name: "online", }, - )?, - user_agent: online_mandate.user_agent, + )?; + StripeMandateRequest { + mandate_type: StripeMandateType::Online { + ip_address: online_mandate + .ip_address + .get_required_value("ip_address") + .change_context( + errors::ConnectorError::MissingRequiredField { + field_name: "ip_address", + }, + )?, + user_agent: online_mandate.user_agent, + }, + } + } + AcceptanceType::Offline => StripeMandateRequest { + mandate_type: StripeMandateType::Offline, }, - } - } - AcceptanceType::Offline => StripeMandateRequest { - mandate_type: StripeMandateType::Offline, - }, - }, - ) + }, + ) + }) }) .transpose()? - .or({ + .or_else(|| { //stripe requires us to send mandate_data while making recurring payment through saved bank debit - //check if payment is done through saved payment method - match &payment_method_types { - //check if payment method is bank debit - Some( - StripePaymentMethodType::Ach - | StripePaymentMethodType::Sepa - | StripePaymentMethodType::Becs - | StripePaymentMethodType::Bacs, - ) => Some(StripeMandateRequest { - mandate_type: StripeMandateType::Offline, - }), - _ => None, + if payment_method.is_some() { + //check if payment is done through saved payment method + match &payment_method_types { + //check if payment method is bank debit + Some( + StripePaymentMethodType::Ach + | StripePaymentMethodType::Sepa + | StripePaymentMethodType::Becs + | StripePaymentMethodType::Bacs, + ) => Some(StripeMandateRequest { + mandate_type: StripeMandateType::Offline, + }), + _ => None, + } + } else { + None } }); From 90e8de18a854a7f83470e84a954ed365078c24c2 Mon Sep 17 00:00:00 2001 From: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Date: Tue, 17 Sep 2024 17:53:41 +0530 Subject: [PATCH 06/48] fix(connector): add field types for bank debits (#5908) Co-authored-by: Chikke Srujan Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 6 ++++ api-reference/openapi_spec.json | 6 ++++ crates/api_models/src/enums.rs | 1 + crates/router/src/configs/defaults.rs | 48 +++++++++++++-------------- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 94cffcd75ac..117c9d2e9e7 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -7190,6 +7190,12 @@ "user_bank" ] }, + { + "type": "string", + "enum": [ + "user_bank_account_number" + ] + }, { "type": "string", "enum": [ diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index cf02aefcf75..c542f3491a3 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -10846,6 +10846,12 @@ "user_bank" ] }, + { + "type": "string", + "enum": [ + "user_bank_account_number" + ] + }, { "type": "string", "enum": [ diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 64af2349f7c..bb2860acb03 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -507,6 +507,7 @@ pub enum FieldType { UserShippingAddressCountry { options: Vec }, UserBlikCode, UserBank, + UserBankAccountNumber, Text, DropDown { options: Vec }, UserDateOfBirth, diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index 659c94423d4..a651d27adcc 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -8773,15 +8773,6 @@ impl Default for super::settings::RequiredFields { value: None, } ), - ( - "billing.phone.country_code".to_string(), - RequiredFieldInfo { - required_field: "payment_method_data.billing.phone.country_code".to_string(), - display_name: "dialing_code".to_string(), - field_type: enums::FieldType::UserPhoneNumberCountryCode, - value: None, - } - ), ] ), } @@ -11069,7 +11060,7 @@ impl Default for super::settings::RequiredFields { RequiredFieldInfo { required_field: "payment_method_data.bank_debit.ach.account_number".to_string(), display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + field_type: enums::FieldType::UserBankAccountNumber, value: None, } ), @@ -11111,7 +11102,7 @@ impl Default for super::settings::RequiredFields { RequiredFieldInfo { required_field: "payment_method_data.bank_debit.ach.account_number".to_string(), display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + field_type: enums::FieldType::UserBankAccountNumber, value: None, } ), @@ -11159,9 +11150,9 @@ impl Default for super::settings::RequiredFields { ( "payment_method_data.bank_debit.sepa.iban".to_string(), RequiredFieldInfo { - required_field: "payment_method_data.bank_debit.bacs.iban".to_string(), - display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + required_field: "payment_method_data.bank_debit.sepa.iban".to_string(), + display_name: "iban".to_string(), + field_type: enums::FieldType::UserIban, value: None, } ), @@ -11202,9 +11193,9 @@ impl Default for super::settings::RequiredFields { ( "payment_method_data.bank_debit.sepa.iban".to_string(), RequiredFieldInfo { - required_field: "payment_method_data.bank_debit.bacs.iban".to_string(), - display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + required_field: "payment_method_data.bank_debit.sepa.iban".to_string(), + display_name: "iban".to_string(), + field_type: enums::FieldType::UserIban, value: None, } ) @@ -11233,12 +11224,21 @@ impl Default for super::settings::RequiredFields { value: None, } ), + ( + "billing.email".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + } + ), ( "payment_method_data.bank_debit.sepa.iban".to_string(), RequiredFieldInfo { - required_field: "payment_method_data.bank_debit.bacs.iban".to_string(), - display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + required_field: "payment_method_data.bank_debit.sepa.iban".to_string(), + display_name: "iban".to_string(), + field_type: enums::FieldType::UserIban, value: None, } ) @@ -11271,7 +11271,7 @@ impl Default for super::settings::RequiredFields { RequiredFieldInfo { required_field: "payment_method_data.bank_debit.bacs.account_number".to_string(), display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + field_type: enums::FieldType::UserBankAccountNumber, value: None, } ), @@ -11343,7 +11343,7 @@ impl Default for super::settings::RequiredFields { RequiredFieldInfo { required_field: "payment_method_data.bank_debit.bacs.account_number".to_string(), display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + field_type: enums::FieldType::UserBankAccountNumber, value: None, } ), @@ -11393,7 +11393,7 @@ impl Default for super::settings::RequiredFields { RequiredFieldInfo { required_field: "payment_method_data.bank_debit.becs.account_number".to_string(), display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + field_type: enums::FieldType::UserBankAccountNumber, value: None, } ), @@ -11445,7 +11445,7 @@ impl Default for super::settings::RequiredFields { RequiredFieldInfo { required_field: "payment_method_data.bank_debit.bacs.account_number".to_string(), display_name: "bank_account_number".to_string(), - field_type: enums::FieldType::Text, + field_type: enums::FieldType::UserBankAccountNumber, value: None, } ), From 1d9e6396522fc0b6cbe0b7ff1be2377098655690 Mon Sep 17 00:00:00 2001 From: Swangi Kumari <85639103+swangi-kumari@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:53:54 +0530 Subject: [PATCH 07/48] refactor(connector): Move connector Volt and Mollie from Router to HyperswitchConnector Trait (#5612) --- Cargo.lock | 1 + .../hyperswitch_connectors/src/connectors.rs | 8 +- .../src/connectors}/mollie.rs | 316 ++++++++--------- .../src/connectors}/mollie/transformers.rs | 162 ++++----- .../src/connectors}/volt.rs | 321 ++++++++---------- .../src/connectors}/volt/transformers.rs | 167 +++++---- .../src/default_implementations.rs | 114 +++++-- .../src/default_implementations_v2.rs | 88 +++-- crates/hyperswitch_connectors/src/types.rs | 9 +- crates/hyperswitch_connectors/src/utils.rs | 18 +- crates/hyperswitch_interfaces/Cargo.toml | 1 + crates/router/src/connector.rs | 25 +- .../connector_integration_v2_impls.rs | 44 --- crates/router/src/core/payments/flows.rs | 60 +--- 14 files changed, 646 insertions(+), 688 deletions(-) rename crates/{router/src/connector => hyperswitch_connectors/src/connectors}/mollie.rs (64%) rename crates/{router/src/connector => hyperswitch_connectors/src/connectors}/mollie/transformers.rs (79%) rename crates/{router/src/connector => hyperswitch_connectors/src/connectors}/volt.rs (69%) rename crates/{router/src/connector => hyperswitch_connectors/src/connectors}/volt/transformers.rs (79%) diff --git a/Cargo.lock b/Cargo.lock index ac324d9c310..11036b42dc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4004,6 +4004,7 @@ dependencies = [ "strum 0.26.3", "thiserror", "time", + "url", ] [[package]] diff --git a/crates/hyperswitch_connectors/src/connectors.rs b/crates/hyperswitch_connectors/src/connectors.rs index 037f91e90ca..48c50936d60 100644 --- a/crates/hyperswitch_connectors/src/connectors.rs +++ b/crates/hyperswitch_connectors/src/connectors.rs @@ -6,6 +6,7 @@ pub mod fiservemea; pub mod fiuu; pub mod globepay; pub mod helcim; +pub mod mollie; pub mod nexixpay; pub mod novalnet; pub mod powertranz; @@ -13,11 +14,12 @@ pub mod stax; pub mod taxjar; pub mod thunes; pub mod tsys; +pub mod volt; pub mod worldline; pub use self::{ bambora::Bambora, bitpay::Bitpay, deutschebank::Deutschebank, fiserv::Fiserv, - fiservemea::Fiservemea, fiuu::Fiuu, globepay::Globepay, helcim::Helcim, nexixpay::Nexixpay, - novalnet::Novalnet, powertranz::Powertranz, stax::Stax, taxjar::Taxjar, thunes::Thunes, - tsys::Tsys, worldline::Worldline, + fiservemea::Fiservemea, fiuu::Fiuu, globepay::Globepay, helcim::Helcim, mollie::Mollie, + nexixpay::Nexixpay, novalnet::Novalnet, powertranz::Powertranz, stax::Stax, taxjar::Taxjar, + thunes::Thunes, tsys::Tsys, volt::Volt, worldline::Worldline, }; diff --git a/crates/router/src/connector/mollie.rs b/crates/hyperswitch_connectors/src/connectors/mollie.rs similarity index 64% rename from crates/router/src/connector/mollie.rs rename to crates/hyperswitch_connectors/src/connectors/mollie.rs index cb001ef7c3f..5066fb422fa 100644 --- a/crates/router/src/connector/mollie.rs +++ b/crates/hyperswitch_connectors/src/connectors/mollie.rs @@ -2,32 +2,48 @@ pub mod transformers; use std::fmt::Debug; -use common_utils::request::RequestContent; +use common_enums::enums; +use common_utils::{ + errors::CustomResult, + ext_traits::BytesExt, + request::{Method, Request, RequestBuilder, RequestContent}, +}; use error_stack::{report, ResultExt}; -use masking::PeekInterface; -use transformers as mollie; - -use crate::{ - configs::settings, - consts, - core::{ - errors::{self, CustomResult}, - payments, +use hyperswitch_domain_models::{ + router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, + router_flow_types::{ + access_token_auth::AccessTokenAuth, + payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, + refunds::{Execute, RSync}, + CompleteAuthorize, }, - events::connector_api_logs::ConnectorEvent, - headers, - services::{ - self, - request::{self, Mask}, - ConnectorIntegration, ConnectorValidation, + router_request_types::{ + AccessTokenRequestData, CompleteAuthorizeData, PaymentMethodTokenizationData, + PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, + PaymentsSyncData, RefundsData, SetupMandateRequestData, }, + router_response_types::{PaymentsResponseData, RefundsResponseData}, types::{ - self, - api::{self, ConnectorCommon, ConnectorCommonExt}, - ErrorResponse, Response, + PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, + PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, TokenizationRouterData, }, - utils::BytesExt, }; +use hyperswitch_interfaces::{ + api::{ + self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorRedirectResponse, + ConnectorValidation, + }, + configs::Connectors, + consts, errors, + events::connector_api_logs::ConnectorEvent, + types::{self, Response}, + webhooks, +}; +use masking::{Mask, PeekInterface}; +use transformers as mollie; + +// use self::mollie::{webhook_headers, VoltWebhookBodyEventType}; +use crate::{constants::headers, types::ResponseRouterData}; #[derive(Debug, Clone)] pub struct Mollie; @@ -52,9 +68,9 @@ where { fn build_headers( &self, - req: &types::RouterData, - _connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.get_auth_header(&req.connector_auth_type) } } @@ -68,14 +84,14 @@ impl ConnectorCommon for Mollie { api::CurrencyUnit::Base } - fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { + fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str { connectors.mollie.base_url.as_ref() } fn get_auth_header( &self, - auth_type: &types::ConnectorAuthType, - ) -> CustomResult)>, errors::ConnectorError> { + auth_type: &ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { let auth = mollie::MollieAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; Ok(vec![( @@ -89,7 +105,7 @@ impl ConnectorCommon for Mollie { res: Response, event_builder: Option<&mut ConnectorEvent>, ) -> CustomResult { - let response: mollie::ErrorResponse = res + let response: mollie::MollieErrorResponse = res .response .parse_struct("MollieErrorResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; @@ -112,28 +128,18 @@ impl ConnectorCommon for Mollie { impl ConnectorValidation for Mollie {} -impl ConnectorIntegration - for Mollie -{ -} +impl ConnectorIntegration for Mollie {} -impl ConnectorIntegration - for Mollie -{ -} +impl ConnectorIntegration for Mollie {} -impl - ConnectorIntegration< - api::PaymentMethodToken, - types::PaymentMethodTokenizationData, - types::PaymentsResponseData, - > for Mollie +impl ConnectorIntegration + for Mollie { fn get_headers( &self, - req: &types::TokenizationRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &TokenizationRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -143,8 +149,8 @@ impl fn get_url( &self, - _req: &types::TokenizationRouterData, - connectors: &settings::Connectors, + _req: &TokenizationRouterData, + connectors: &Connectors, ) -> CustomResult { let base_url = connectors .mollie @@ -156,20 +162,20 @@ impl fn get_request_body( &self, - req: &types::TokenizationRouterData, - _connectors: &settings::Connectors, + req: &TokenizationRouterData, + _connectors: &Connectors, ) -> CustomResult { let connector_req = mollie::MollieCardTokenRequest::try_from(req)?; Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request( &self, - req: &types::TokenizationRouterData, - connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { + req: &TokenizationRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Post) + RequestBuilder::new() + .method(Method::Post) .url(&types::TokenizationType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::TokenizationType::get_headers(self, req, connectors)?) @@ -182,12 +188,12 @@ impl fn handle_response( &self, - data: &types::TokenizationRouterData, + data: &TokenizationRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult + ) -> CustomResult where - types::PaymentsResponseData: Clone, + PaymentsResponseData: Clone, { let response: mollie::MollieCardTokenResponse = res .response @@ -197,7 +203,7 @@ impl event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); - types::RouterData::try_from(types::ResponseRouterData { + RouterData::try_from(ResponseRouterData { response, data: data.clone(), http_code: res.status_code, @@ -213,22 +219,12 @@ impl } } -impl - ConnectorIntegration< - api::SetupMandate, - types::SetupMandateRequestData, - types::PaymentsResponseData, - > for Mollie -{ +impl ConnectorIntegration for Mollie { fn build_request( &self, - _req: &types::RouterData< - api::SetupMandate, - types::SetupMandateRequestData, - types::PaymentsResponseData, - >, - _connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { + _req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Err( errors::ConnectorError::NotImplemented("Setup Mandate flow for Mollie".to_string()) .into(), @@ -236,29 +232,27 @@ impl } } -impl ConnectorIntegration - for Mollie -{ +impl ConnectorIntegration for Mollie { fn get_headers( &self, - req: &types::PaymentsAuthorizeRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } fn get_url( &self, - _req: &types::PaymentsAuthorizeRouterData, - connectors: &settings::Connectors, + _req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, ) -> CustomResult { Ok(format!("{}payments", self.base_url(connectors))) } fn get_request_body( &self, - req: &types::PaymentsAuthorizeRouterData, - _connectors: &settings::Connectors, + req: &PaymentsAuthorizeRouterData, + _connectors: &Connectors, ) -> CustomResult { let router_obj = mollie::MollieRouterData::try_from(( &self.get_currency_unit(), @@ -272,12 +266,12 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Post) + RequestBuilder::new() + .method(Method::Post) .url(&types::PaymentsAuthorizeType::get_url( self, req, connectors, )?) @@ -294,10 +288,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: mollie::MolliePaymentsResponse = res .response .parse_struct("MolliePaymentsResponse") @@ -306,7 +300,7 @@ impl ConnectorIntegration for Mollie +impl ConnectorIntegration + for Mollie { } -impl ConnectorIntegration - for Mollie -{ +impl ConnectorIntegration for Mollie { fn get_headers( &self, - req: &types::PaymentsSyncRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } fn get_url( &self, - req: &types::PaymentsSyncRouterData, - connectors: &settings::Connectors, + req: &PaymentsSyncRouterData, + connectors: &Connectors, ) -> CustomResult { Ok(format!( "{}payments/{}", @@ -359,12 +347,12 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Get) + RequestBuilder::new() + .method(Method::Get) .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) @@ -374,10 +362,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: mollie::MolliePaymentsResponse = res .response .parse_struct("mollie PaymentsSyncResponse") @@ -386,7 +374,7 @@ impl ConnectorIntegration - for Mollie -{ +impl ConnectorIntegration for Mollie { fn build_request( &self, - _req: &types::PaymentsCaptureRouterData, - _connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { + _req: &PaymentsCaptureRouterData, + _connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::FlowNotSupported { flow: "Capture".to_string(), connector: self.id().to_string(), @@ -418,14 +404,12 @@ impl ConnectorIntegration - for Mollie -{ +impl ConnectorIntegration for Mollie { fn build_request( &self, - _req: &types::PaymentsCancelRouterData, - _connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { + _req: &PaymentsCancelRouterData, + _connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Err(errors::ConnectorError::FlowNotSupported { flow: "Void".to_string(), connector: self.id().to_string(), @@ -434,12 +418,12 @@ impl ConnectorIntegration for Mollie { +impl ConnectorIntegration for Mollie { fn get_headers( &self, - req: &types::RefundsRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -449,8 +433,8 @@ impl ConnectorIntegration, - connectors: &settings::Connectors, + req: &RefundsRouterData, + connectors: &Connectors, ) -> CustomResult { Ok(format!( "{}payments/{}/refunds", @@ -461,8 +445,8 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + req: &RefundsRouterData, + _connectors: &Connectors, ) -> CustomResult { let router_obj = mollie::MollieRouterData::try_from(( &self.get_currency_unit(), @@ -476,11 +460,11 @@ impl ConnectorIntegration, - connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { - let request = services::RequestBuilder::new() - .method(services::Method::Post) + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Post) .url(&types::RefundExecuteType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundExecuteType::get_headers( @@ -495,10 +479,10 @@ impl ConnectorIntegration, + data: &RefundsRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult, errors::ConnectorError> { + ) -> CustomResult, errors::ConnectorError> { let response: mollie::RefundResponse = res .response .parse_struct("MollieRefundResponse") @@ -507,7 +491,7 @@ impl ConnectorIntegration for Mollie { +impl ConnectorIntegration for Mollie { fn get_headers( &self, - req: &types::RefundSyncRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &RefundSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -538,8 +522,8 @@ impl ConnectorIntegration CustomResult { let connector_refund_id = req .request @@ -556,12 +540,12 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { + req: &RefundSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Get) + RequestBuilder::new() + .method(Method::Get) .url(&types::RefundSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundSyncType::get_headers(self, req, connectors)?) @@ -571,10 +555,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: mollie::RefundResponse = res .response .parse_struct("MollieRefundSyncResponse") @@ -583,7 +567,7 @@ impl ConnectorIntegration, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult { Err(report!(errors::ConnectorError::WebhooksNotImplemented)) } fn get_webhook_event_type( &self, - _request: &api::IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { - Ok(api::IncomingWebhookEvent::EventNotSupported) + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported) } fn get_webhook_resource_object( &self, - _request: &api::IncomingWebhookRequestDetails<'_>, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { Err(report!(errors::ConnectorError::WebhooksNotImplemented)) } } -impl services::ConnectorRedirectResponse for Mollie { +impl ConnectorRedirectResponse for Mollie { fn get_flow_type( &self, _query_params: &str, _json_payload: Option, - action: services::PaymentAction, - ) -> CustomResult { + action: enums::PaymentAction, + ) -> CustomResult { match action { - services::PaymentAction::PSync - | services::PaymentAction::CompleteAuthorize - | services::PaymentAction::PaymentAuthenticateCompleteAuthorize => { - Ok(payments::CallConnectorAction::Trigger) + enums::PaymentAction::PSync + | enums::PaymentAction::CompleteAuthorize + | enums::PaymentAction::PaymentAuthenticateCompleteAuthorize => { + Ok(enums::CallConnectorAction::Trigger) } } } diff --git a/crates/router/src/connector/mollie/transformers.rs b/crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs similarity index 79% rename from crates/router/src/connector/mollie/transformers.rs rename to crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs index f755ca6029b..d0525921632 100644 --- a/crates/router/src/connector/mollie/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs @@ -1,20 +1,25 @@ -use api_models::payments; use cards::CardNumber; -use common_utils::pii::Email; -use diesel_models::enums; +use common_enums::enums; +use common_utils::{pii::Email, request::Method}; +use hyperswitch_domain_models::{ + payment_method_data::{BankDebitData, BankRedirectData, PaymentMethodData, WalletData}, + router_data::{ConnectorAuthType, PaymentMethodToken, RouterData}, + router_request_types::ResponseId, + router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, + types, +}; +use hyperswitch_interfaces::{api, errors}; use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; use url::Url; use crate::{ - connector::utils::{ - self, AddressDetailsData, BrowserInformationData, CardData, - PaymentMethodTokenizationRequestData, PaymentsAuthorizeRequestData, RouterData, - }, - core::errors, - services, types, - types::{domain, storage::enums as storage_enums}, + types::{RefundsResponseRouterData, ResponseRouterData}, unimplemented_payment_method, + utils::{ + self, AddressDetailsData, BrowserInformationData, CardData as CardDataUtil, + PaymentMethodTokenizationRequestData, PaymentsAuthorizeRequestData, RouterData as _, + }, }; type Error = error_stack::Report; @@ -25,20 +30,13 @@ pub struct MollieRouterData { pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for MollieRouterData -{ +impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for MollieRouterData { type Error = error_stack::Report; fn try_from( (currency_unit, currency, amount, router_data): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, + &api::CurrencyUnit, + enums::Currency, i64, T, ), @@ -61,7 +59,7 @@ pub struct MolliePaymentsRequest { webhook_url: String, locale: Option, #[serde(flatten)] - payment_method_data: PaymentMethodData, + payment_method_data: MolliePaymentMethodData, metadata: Option, sequence_type: SequenceType, mandate_id: Option>, @@ -76,7 +74,7 @@ pub struct Amount { #[derive(Debug, Serialize)] #[serde(tag = "method")] #[serde(rename_all = "lowercase")] -pub enum PaymentMethodData { +pub enum MolliePaymentMethodData { Applepay(Box), Eps, Giropay, @@ -169,15 +167,15 @@ impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MollieP { enums::CaptureMethod::Automatic => { match &item.router_data.request.payment_method_data { - domain::PaymentMethodData::Card(_) => { + PaymentMethodData::Card(_) => { let pm_token = item.router_data.get_payment_method_token()?; - Ok(PaymentMethodData::CreditCard(Box::new( + Ok(MolliePaymentMethodData::CreditCard(Box::new( CreditCardMethodData { billing_address: get_billing_details(item.router_data)?, shipping_address: get_shipping_details(item.router_data)?, card_token: Some(match pm_token { - types::PaymentMethodToken::Token(token) => token, - types::PaymentMethodToken::ApplePayDecrypt(_) => { + PaymentMethodToken::Token(token) => token, + PaymentMethodToken::ApplePayDecrypt(_) => { Err(unimplemented_payment_method!( "Apple Pay", "Simplified", @@ -188,14 +186,14 @@ impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MollieP }, ))) } - domain::PaymentMethodData::BankRedirect(ref redirect_data) => { - PaymentMethodData::try_from((item.router_data, redirect_data)) + PaymentMethodData::BankRedirect(ref redirect_data) => { + MolliePaymentMethodData::try_from((item.router_data, redirect_data)) } - domain::PaymentMethodData::Wallet(ref wallet_data) => { + PaymentMethodData::Wallet(ref wallet_data) => { get_payment_method_for_wallet(item.router_data, wallet_data) } - domain::PaymentMethodData::BankDebit(ref directdebit_data) => { - PaymentMethodData::try_from((directdebit_data, item.router_data)) + PaymentMethodData::BankDebit(ref directdebit_data) => { + MolliePaymentMethodData::try_from((directdebit_data, item.router_data)) } _ => Err( errors::ConnectorError::NotImplemented("Payment Method".to_string()).into(), @@ -230,47 +228,39 @@ impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MollieP } } -impl - TryFrom<( - &types::PaymentsAuthorizeRouterData, - &domain::BankRedirectData, - )> for PaymentMethodData -{ +impl TryFrom<(&types::PaymentsAuthorizeRouterData, &BankRedirectData)> for MolliePaymentMethodData { type Error = Error; fn try_from( - (item, value): ( - &types::PaymentsAuthorizeRouterData, - &domain::BankRedirectData, - ), + (item, value): (&types::PaymentsAuthorizeRouterData, &BankRedirectData), ) -> Result { match value { - domain::BankRedirectData::Eps { .. } => Ok(Self::Eps), - domain::BankRedirectData::Giropay { .. } => Ok(Self::Giropay), - domain::BankRedirectData::Ideal { .. } => { + BankRedirectData::Eps { .. } => Ok(Self::Eps), + BankRedirectData::Giropay { .. } => Ok(Self::Giropay), + BankRedirectData::Ideal { .. } => { Ok(Self::Ideal(Box::new(IdealMethodData { // To do if possible this should be from the payment request issuer: None, }))) } - domain::BankRedirectData::Sofort { .. } => Ok(Self::Sofort), - domain::BankRedirectData::Przelewy24 { .. } => { + BankRedirectData::Sofort { .. } => Ok(Self::Sofort), + BankRedirectData::Przelewy24 { .. } => { Ok(Self::Przelewy24(Box::new(Przelewy24MethodData { billing_email: item.get_optional_billing_email(), }))) } - domain::BankRedirectData::BancontactCard { .. } => Ok(Self::Bancontact), + BankRedirectData::BancontactCard { .. } => Ok(Self::Bancontact), _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), } } } -impl TryFrom<(&domain::BankDebitData, &types::PaymentsAuthorizeRouterData)> for PaymentMethodData { +impl TryFrom<(&BankDebitData, &types::PaymentsAuthorizeRouterData)> for MolliePaymentMethodData { type Error = Error; fn try_from( - (bank_debit_data, item): (&domain::BankDebitData, &types::PaymentsAuthorizeRouterData), + (bank_debit_data, item): (&BankDebitData, &types::PaymentsAuthorizeRouterData), ) -> Result { match bank_debit_data { - domain::BankDebitData::SepaBankDebit { iban, .. } => { + BankDebitData::SepaBankDebit { iban, .. } => { Ok(Self::DirectDebit(Box::new(DirectDebitMethodData { consumer_name: item.get_optional_billing_full_name(), consumer_account: iban.clone(), @@ -297,7 +287,7 @@ impl TryFrom<&types::TokenizationRouterData> for MollieCardTokenRequest { type Error = error_stack::Report; fn try_from(item: &types::TokenizationRouterData) -> Result { match item.request.payment_method_data.clone() { - domain::PaymentMethodData::Card(ccard) => { + PaymentMethodData::Card(ccard) => { let auth = MollieAuthType::try_from(&item.connector_auth_type)?; let card_holder = item .get_optional_billing_full_name() @@ -334,20 +324,20 @@ impl TryFrom<&types::TokenizationRouterData> for MollieCardTokenRequest { fn get_payment_method_for_wallet( item: &types::PaymentsAuthorizeRouterData, - wallet_data: &domain::WalletData, -) -> Result { + wallet_data: &WalletData, +) -> Result { match wallet_data { - domain::WalletData::PaypalRedirect { .. } => { - Ok(PaymentMethodData::Paypal(Box::new(PaypalMethodData { + WalletData::PaypalRedirect { .. } => Ok(MolliePaymentMethodData::Paypal(Box::new( + PaypalMethodData { billing_address: get_billing_details(item)?, shipping_address: get_shipping_details(item)?, - }))) - } - domain::WalletData::ApplePay(applepay_wallet_data) => { - Ok(PaymentMethodData::Applepay(Box::new(ApplePayMethodData { + }, + ))), + WalletData::ApplePay(applepay_wallet_data) => Ok(MolliePaymentMethodData::Applepay( + Box::new(ApplePayMethodData { apple_pay_payment_token: Secret::new(applepay_wallet_data.payment_data.to_owned()), - }))) - } + }), + )), _ => Err(errors::ConnectorError::NotImplemented("Payment Method".to_string()).into()), } } @@ -371,7 +361,7 @@ fn get_billing_details( } fn get_address_details( - address: Option<&payments::AddressDetails>, + address: Option<&api_models::payments::AddressDetails>, ) -> Result, Error> { let address_details = match address { Some(address) => { @@ -467,15 +457,15 @@ pub struct MollieAuthType { pub(super) profile_token: Option>, } -impl TryFrom<&types::ConnectorAuthType> for MollieAuthType { +impl TryFrom<&ConnectorAuthType> for MollieAuthType { type Error = Error; - fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + fn try_from(auth_type: &ConnectorAuthType) -> Result { match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + ConnectorAuthType::HeaderKey { api_key } => Ok(Self { api_key: api_key.to_owned(), profile_token: None, }), - types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self { + ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self { api_key: api_key.to_owned(), profile_token: Some(key1.to_owned()), }), @@ -490,20 +480,17 @@ pub struct MollieCardTokenResponse { card_token: Secret, } -impl - TryFrom> - for types::RouterData +impl TryFrom> + for RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: ResponseRouterData, ) -> Result { Ok(Self { - status: storage_enums::AttemptStatus::Pending, - payment_method_token: Some(types::PaymentMethodToken::Token( - item.response.card_token.clone(), - )), - response: Ok(types::PaymentsResponseData::TokenizationResponse { + status: enums::AttemptStatus::Pending, + payment_method_token: Some(PaymentMethodToken::Token(item.response.card_token.clone())), + response: Ok(PaymentsResponseData::TokenizationResponse { token: item.response.card_token.expose(), }), ..item.data @@ -511,23 +498,22 @@ impl } } -impl - TryFrom> - for types::RouterData +impl TryFrom> + for RouterData { type Error = Error; fn try_from( - item: types::ResponseRouterData, + item: ResponseRouterData, ) -> Result { let url = item .response .links .checkout - .map(|link| services::RedirectForm::from((link.href, services::Method::Get))); + .map(|link| RedirectForm::from((link.href, Method::Get))); Ok(Self { status: enums::AttemptStatus::from(item.response.status), - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()), redirection_data: url, mandate_reference: None, connector_metadata: None, @@ -608,15 +594,11 @@ impl From for enums::RefundStatus { } } -impl TryFrom> - for types::RefundsRouterData -{ +impl TryFrom> for types::RefundsRouterData { type Error = Error; - fn try_from( - item: types::RefundsResponseRouterData, - ) -> Result { + fn try_from(item: RefundsResponseRouterData) -> Result { Ok(Self { - response: Ok(types::RefundsResponseData { + response: Ok(RefundsResponseData { connector_refund_id: item.response.id, refund_status: enums::RefundStatus::from(item.response.status), }), @@ -626,7 +608,7 @@ impl TryFrom> } #[derive(Debug, Deserialize, Serialize)] -pub struct ErrorResponse { +pub struct MollieErrorResponse { pub status: u16, pub title: Option, pub detail: String, diff --git a/crates/router/src/connector/volt.rs b/crates/hyperswitch_connectors/src/connectors/volt.rs similarity index 69% rename from crates/router/src/connector/volt.rs rename to crates/hyperswitch_connectors/src/connectors/volt.rs index 202dcba7ed1..e9024897014 100644 --- a/crates/router/src/connector/volt.rs +++ b/crates/hyperswitch_connectors/src/connectors/volt.rs @@ -1,36 +1,48 @@ pub mod transformers; +use api_models::webhooks::IncomingWebhookEvent; use common_utils::{ crypto, + errors::CustomResult, ext_traits::ByteSliceExt, - request::RequestContent, + request::{Method, Request, RequestBuilder, RequestContent}, types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, }; use error_stack::ResultExt; -use masking::{ExposeInterface, PeekInterface}; -use transformers as volt; - -use self::transformers::webhook_headers; -use super::{ - utils, - utils::{self as connector_utils}, -}; -use crate::{ - configs::settings, - core::errors::{self, CustomResult}, - events::connector_api_logs::ConnectorEvent, - headers, - services::{ - self, - request::{self, Mask}, - ConnectorIntegration, ConnectorValidation, +use hyperswitch_domain_models::{ + router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, + router_flow_types::{ + access_token_auth::AccessTokenAuth, + payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, + refunds::{Execute, RSync}, }, + router_request_types::{ + AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, + PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, + RefundsData, SetupMandateRequestData, + }, + router_response_types::{PaymentsResponseData, RefundsResponseData}, types::{ - self, - api::{self, ConnectorCommon, ConnectorCommonExt}, - ErrorResponse, Response, + PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, + RefreshTokenRouterData, RefundsRouterData, }, - utils::BytesExt, +}; +use hyperswitch_interfaces::{ + api::{self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorValidation}, + configs::Connectors, + errors, + events::connector_api_logs::ConnectorEvent, + types::{self, PaymentsAuthorizeType, RefreshTokenType, Response}, + webhooks, +}; +use masking::{ExposeInterface, Mask, PeekInterface}; +use transformers as volt; + +use self::volt::{webhook_headers, VoltWebhookBodyEventType}; +use crate::{ + constants::headers, + types::ResponseRouterData, + utils::{self}, }; #[derive(Clone)] @@ -59,12 +71,8 @@ impl api::RefundExecute for Volt {} impl api::RefundSync for Volt {} impl api::PaymentToken for Volt {} -impl - ConnectorIntegration< - api::PaymentMethodToken, - types::PaymentMethodTokenizationData, - types::PaymentsResponseData, - > for Volt +impl ConnectorIntegration + for Volt { // Not Implemented (R) } @@ -75,12 +83,12 @@ where { fn build_headers( &self, - req: &types::RouterData, - _connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { let mut header = vec![( headers::CONTENT_TYPE.to_string(), - types::PaymentsAuthorizeType::get_content_type(self) + PaymentsAuthorizeType::get_content_type(self) .to_string() .into(), )]; @@ -110,14 +118,14 @@ impl ConnectorCommon for Volt { "application/json" } - fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { + fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str { connectors.volt.base_url.as_ref() } fn get_auth_header( &self, - auth_type: &types::ConnectorAuthType, - ) -> CustomResult)>, errors::ConnectorError> { + auth_type: &ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { let auth = volt::VoltAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; Ok(vec![( @@ -163,19 +171,15 @@ impl ConnectorValidation for Volt { //TODO: implement functions when support enabled } -impl ConnectorIntegration - for Volt -{ +impl ConnectorIntegration for Volt { //TODO: implement sessions flow } -impl ConnectorIntegration - for Volt -{ +impl ConnectorIntegration for Volt { fn get_url( &self, - _req: &types::RefreshTokenRouterData, - connectors: &settings::Connectors, + _req: &RefreshTokenRouterData, + connectors: &Connectors, ) -> CustomResult { Ok(format!("{}oauth", self.base_url(connectors))) } @@ -185,21 +189,19 @@ impl ConnectorIntegration CustomResult)>, errors::ConnectorError> { + _req: &RefreshTokenRouterData, + _connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { Ok(vec![( headers::CONTENT_TYPE.to_string(), - types::RefreshTokenType::get_content_type(self) - .to_string() - .into(), + RefreshTokenType::get_content_type(self).to_string().into(), )]) } fn get_request_body( &self, - req: &types::RefreshTokenRouterData, - _connectors: &settings::Connectors, + req: &RefreshTokenRouterData, + _connectors: &Connectors, ) -> CustomResult { let connector_req = volt::VoltAuthUpdateRequest::try_from(req)?; @@ -208,18 +210,16 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { + req: &RefreshTokenRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { let req = Some( - services::RequestBuilder::new() - .method(services::Method::Post) + RequestBuilder::new() + .method(Method::Post) .attach_default_headers() - .headers(types::RefreshTokenType::get_headers(self, req, connectors)?) - .url(&types::RefreshTokenType::get_url(self, req, connectors)?) - .set_body(types::RefreshTokenType::get_request_body( - self, req, connectors, - )?) + .headers(RefreshTokenType::get_headers(self, req, connectors)?) + .url(&RefreshTokenType::get_url(self, req, connectors)?) + .set_body(RefreshTokenType::get_request_body(self, req, connectors)?) .build(), ); Ok(req) @@ -227,10 +227,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: volt::VoltAuthUpdateResponse = res .response .parse_struct("Volt VoltAuthUpdateResponse") @@ -239,7 +239,7 @@ impl ConnectorIntegration for Volt -{ +impl ConnectorIntegration for Volt { fn build_request( &self, - _req: &types::RouterData< - api::SetupMandate, - types::SetupMandateRequestData, - types::PaymentsResponseData, - >, - _connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { + _req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Err( errors::ConnectorError::NotImplemented("Setup Mandate flow for Volt".to_string()) .into(), @@ -294,14 +284,12 @@ impl } } -impl ConnectorIntegration - for Volt -{ +impl ConnectorIntegration for Volt { fn get_headers( &self, - req: &types::PaymentsAuthorizeRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -311,18 +299,18 @@ impl ConnectorIntegration CustomResult { Ok(format!("{}v2/payments", self.base_url(connectors))) } fn get_request_body( &self, - req: &types::PaymentsAuthorizeRouterData, - _connectors: &settings::Connectors, + req: &PaymentsAuthorizeRouterData, + _connectors: &Connectors, ) -> CustomResult { - let amount = connector_utils::convert_amount( + let amount = utils::convert_amount( self.amount_converter, req.request.minor_amount, req.request.currency, @@ -334,20 +322,16 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Post) - .url(&types::PaymentsAuthorizeType::get_url( - self, req, connectors, - )?) + RequestBuilder::new() + .method(Method::Post) + .url(&PaymentsAuthorizeType::get_url(self, req, connectors)?) .attach_default_headers() - .headers(types::PaymentsAuthorizeType::get_headers( - self, req, connectors, - )?) - .set_body(types::PaymentsAuthorizeType::get_request_body( + .headers(PaymentsAuthorizeType::get_headers(self, req, connectors)?) + .set_body(PaymentsAuthorizeType::get_request_body( self, req, connectors, )?) .build(), @@ -356,10 +340,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: volt::VoltPaymentsResponse = res .response .parse_struct("Volt PaymentsAuthorizeResponse") @@ -368,7 +352,7 @@ impl ConnectorIntegration - for Volt -{ +impl ConnectorIntegration for Volt { fn get_headers( &self, - req: &types::PaymentsSyncRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -401,8 +383,8 @@ impl ConnectorIntegration CustomResult { let connector_payment_id = req .request @@ -417,12 +399,12 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Get) + RequestBuilder::new() + .method(Method::Get) .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) @@ -432,10 +414,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: volt::VoltPaymentsResponseData = res .response .parse_struct("volt PaymentsSyncResponse") @@ -444,7 +426,7 @@ impl ConnectorIntegration - for Volt -{ +impl ConnectorIntegration for Volt { fn get_headers( &self, - req: &types::PaymentsCaptureRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &PaymentsCaptureRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -477,28 +457,28 @@ impl ConnectorIntegration CustomResult { Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) } fn get_request_body( &self, - _req: &types::PaymentsCaptureRouterData, - _connectors: &settings::Connectors, + _req: &PaymentsCaptureRouterData, + _connectors: &Connectors, ) -> CustomResult { Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) } fn build_request( &self, - req: &types::PaymentsCaptureRouterData, - connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { + req: &PaymentsCaptureRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Post) + RequestBuilder::new() + .method(Method::Post) .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsCaptureType::get_headers( @@ -513,10 +493,10 @@ impl ConnectorIntegration, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: volt::VoltPaymentsResponse = res .response .parse_struct("Volt PaymentsCaptureResponse") @@ -525,7 +505,7 @@ impl ConnectorIntegration - for Volt -{ -} +impl ConnectorIntegration for Volt {} -impl ConnectorIntegration for Volt { +impl ConnectorIntegration for Volt { fn get_headers( &self, - req: &types::RefundsRouterData, - connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) } @@ -561,8 +538,8 @@ impl ConnectorIntegration, - connectors: &settings::Connectors, + req: &RefundsRouterData, + connectors: &Connectors, ) -> CustomResult { let connector_payment_id = req.request.connector_transaction_id.clone(); Ok(format!( @@ -573,10 +550,10 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + req: &RefundsRouterData, + _connectors: &Connectors, ) -> CustomResult { - let amount = connector_utils::convert_amount( + let amount = utils::convert_amount( self.amount_converter, req.request.minor_refund_amount, req.request.currency, @@ -588,11 +565,11 @@ impl ConnectorIntegration, - connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { - let request = services::RequestBuilder::new() - .method(services::Method::Post) + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Post) .url(&types::RefundExecuteType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::RefundExecuteType::get_headers( @@ -607,10 +584,10 @@ impl ConnectorIntegration, + data: &RefundsRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult, errors::ConnectorError> { + ) -> CustomResult, errors::ConnectorError> { let response: volt::RefundResponse = res .response .parse_struct("volt RefundResponse") @@ -619,7 +596,7 @@ impl ConnectorIntegration for Volt { +impl ConnectorIntegration for Volt { //Volt does not support Refund Sync } #[async_trait::async_trait] -impl api::IncomingWebhook for Volt { +impl webhooks::IncomingWebhook for Volt { fn get_webhook_source_verification_algorithm( &self, - _request: &api::IncomingWebhookRequestDetails<'_>, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { Ok(Box::new(crypto::HmacSha256)) } fn get_webhook_source_verification_signature( &self, - request: &api::IncomingWebhookRequestDetails<'_>, + request: &webhooks::IncomingWebhookRequestDetails<'_>, _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, ) -> CustomResult, errors::ConnectorError> { let signature = @@ -663,7 +640,7 @@ impl api::IncomingWebhook for Volt { fn get_webhook_source_verification_message( &self, - request: &api::IncomingWebhookRequestDetails<'_>, + request: &webhooks::IncomingWebhookRequestDetails<'_>, _merchant_id: &common_utils::id_type::MerchantId, _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, ) -> CustomResult, errors::ConnectorError> { @@ -685,8 +662,8 @@ impl api::IncomingWebhook for Volt { fn get_webhook_object_reference_id( &self, - request: &api::IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { + request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { let parsed_webhook_response = request .body .parse_struct::("VoltRefundWebhookBodyReference") @@ -726,22 +703,22 @@ impl api::IncomingWebhook for Volt { fn get_webhook_event_type( &self, - request: &api::IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { + request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { if request.body.is_empty() { - Ok(api::IncomingWebhookEvent::EndpointVerification) + Ok(IncomingWebhookEvent::EndpointVerification) } else { - let payload: volt::VoltWebhookBodyEventType = request + let payload: VoltWebhookBodyEventType = request .body .parse_struct("VoltWebhookBodyEventType") .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; - Ok(api::IncomingWebhookEvent::from(payload)) + Ok(IncomingWebhookEvent::from(payload)) } } fn get_webhook_resource_object( &self, - request: &api::IncomingWebhookRequestDetails<'_>, + request: &webhooks::IncomingWebhookRequestDetails<'_>, ) -> CustomResult, errors::ConnectorError> { let details: volt::VoltWebhookObjectResource = request .body diff --git a/crates/router/src/connector/volt/transformers.rs b/crates/hyperswitch_connectors/src/connectors/volt/transformers.rs similarity index 79% rename from crates/router/src/connector/volt/transformers.rs rename to crates/hyperswitch_connectors/src/connectors/volt/transformers.rs index 451c70279cc..1093ef4e6f0 100644 --- a/crates/router/src/connector/volt/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/volt/transformers.rs @@ -1,14 +1,20 @@ -use common_utils::{id_type, pii::Email, types::MinorUnit}; -use diesel_models::enums; +use common_enums::enums; +use common_utils::{id_type, pii::Email, request::Method, types::MinorUnit}; +use hyperswitch_domain_models::{ + payment_method_data::{BankRedirectData, PaymentMethodData}, + router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, + router_flow_types::refunds::Execute, + router_request_types::ResponseId, + router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, + types, +}; +use hyperswitch_interfaces::{consts, errors}; use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::{self, AddressDetailsData, RouterData}, - consts, - core::errors, - services, - types::{self, api, domain, storage::enums as storage_enums}, + types::{RefreshTokenRouterData, RefundsResponseRouterData, ResponseRouterData}, + utils::{self, AddressDetailsData, RouterData as _}, }; const PASSWORD: &str = "password"; @@ -37,7 +43,7 @@ pub mod webhook_headers { #[serde(rename_all = "camelCase")] pub struct VoltPaymentsRequest { amount: MinorUnit, - currency_code: storage_enums::Currency, + currency_code: enums::Currency, #[serde(rename = "type")] transaction_type: TransactionType, merchant_internal_reference: String, @@ -72,8 +78,8 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme item: &VoltRouterData<&types::PaymentsAuthorizeRouterData>, ) -> Result { match item.router_data.request.payment_method_data.clone() { - domain::PaymentMethodData::BankRedirect(ref bank_redirect) => match bank_redirect { - domain::BankRedirectData::OpenBankingUk { .. } => { + PaymentMethodData::BankRedirect(ref bank_redirect) => match bank_redirect { + BankRedirectData::OpenBankingUk { .. } => { let amount = item.amount; let currency_code = item.router_data.request.currency; let merchant_internal_reference = @@ -104,50 +110,48 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme transaction_type, }) } - domain::BankRedirectData::BancontactCard { .. } - | domain::BankRedirectData::Bizum {} - | domain::BankRedirectData::Blik { .. } - | domain::BankRedirectData::Eps { .. } - | domain::BankRedirectData::Giropay { .. } - | domain::BankRedirectData::Ideal { .. } - | domain::BankRedirectData::Interac { .. } - | domain::BankRedirectData::OnlineBankingCzechRepublic { .. } - | domain::BankRedirectData::OnlineBankingFinland { .. } - | domain::BankRedirectData::OnlineBankingPoland { .. } - | domain::BankRedirectData::OnlineBankingSlovakia { .. } - | domain::BankRedirectData::Przelewy24 { .. } - | domain::BankRedirectData::Sofort { .. } - | domain::BankRedirectData::Trustly { .. } - | domain::BankRedirectData::OnlineBankingFpx { .. } - | domain::BankRedirectData::OnlineBankingThailand { .. } - | domain::BankRedirectData::LocalBankRedirect {} => { + BankRedirectData::BancontactCard { .. } + | BankRedirectData::Bizum {} + | BankRedirectData::Blik { .. } + | BankRedirectData::Eps { .. } + | BankRedirectData::Giropay { .. } + | BankRedirectData::Ideal { .. } + | BankRedirectData::Interac { .. } + | BankRedirectData::OnlineBankingCzechRepublic { .. } + | BankRedirectData::OnlineBankingFinland { .. } + | BankRedirectData::OnlineBankingPoland { .. } + | BankRedirectData::OnlineBankingSlovakia { .. } + | BankRedirectData::Przelewy24 { .. } + | BankRedirectData::Sofort { .. } + | BankRedirectData::Trustly { .. } + | BankRedirectData::OnlineBankingFpx { .. } + | BankRedirectData::OnlineBankingThailand { .. } + | BankRedirectData::LocalBankRedirect {} => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Volt"), ) .into()) } }, - domain::PaymentMethodData::Card(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::RealTimePayment(_) - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Volt"), - ) - .into()) - } + PaymentMethodData::Card(_) + | PaymentMethodData::CardRedirect(_) + | PaymentMethodData::Wallet(_) + | PaymentMethodData::PayLater(_) + | PaymentMethodData::BankDebit(_) + | PaymentMethodData::BankTransfer(_) + | PaymentMethodData::Crypto(_) + | PaymentMethodData::MandatePayment + | PaymentMethodData::Reward + | PaymentMethodData::RealTimePayment(_) + | PaymentMethodData::Upi(_) + | PaymentMethodData::Voucher(_) + | PaymentMethodData::GiftCard(_) + | PaymentMethodData::OpenBanking(_) + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Volt"), + ) + .into()), } } } @@ -161,9 +165,9 @@ pub struct VoltAuthUpdateRequest { password: Secret, } -impl TryFrom<&types::RefreshTokenRouterData> for VoltAuthUpdateRequest { +impl TryFrom<&RefreshTokenRouterData> for VoltAuthUpdateRequest { type Error = error_stack::Report; - fn try_from(item: &types::RefreshTokenRouterData) -> Result { + fn try_from(item: &RefreshTokenRouterData) -> Result { let auth = VoltAuthType::try_from(&item.connector_auth_type)?; Ok(Self { grant_type: PASSWORD.to_string(), @@ -183,15 +187,15 @@ pub struct VoltAuthUpdateResponse { pub refresh_token: Secret, } -impl TryFrom> - for types::RouterData +impl TryFrom> + for RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: ResponseRouterData, ) -> Result { Ok(Self { - response: Ok(types::AccessToken { + response: Ok(AccessToken { token: item.response.access_token, expires: item.response.expires_in, }), @@ -207,11 +211,11 @@ pub struct VoltAuthType { pub(super) client_secret: Secret, } -impl TryFrom<&types::ConnectorAuthType> for VoltAuthType { +impl TryFrom<&ConnectorAuthType> for VoltAuthType { type Error = error_stack::Report; - fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + fn try_from(auth_type: &ConnectorAuthType) -> Result { match auth_type { - types::ConnectorAuthType::MultiAuthKey { + ConnectorAuthType::MultiAuthKey { api_key, key1, api_secret, @@ -253,24 +257,23 @@ pub struct VoltPaymentsResponse { id: String, } -impl - TryFrom> - for types::RouterData +impl TryFrom> + for RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: ResponseRouterData, ) -> Result { let url = item.response.checkout_url; - let redirection_data = Some(services::RedirectForm::Form { + let redirection_data = Some(RedirectForm::Form { endpoint: url, - method: services::Method::Get, + method: Method::Get, form_fields: Default::default(), }); Ok(Self { status: enums::AttemptStatus::AuthenticationPending, - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()), redirection_data, mandate_reference: None, connector_metadata: None, @@ -319,18 +322,12 @@ pub struct VoltPsyncResponse { merchant_internal_reference: Option, } -impl - TryFrom> - for types::RouterData +impl TryFrom> + for RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData< - F, - VoltPaymentsResponseData, - T, - types::PaymentsResponseData, - >, + item: ResponseRouterData, ) -> Result { match item.response { VoltPaymentsResponseData::PsyncResponse(payment_response) => { @@ -338,7 +335,7 @@ impl Ok(Self { status, response: if is_payment_failure(status) { - Err(types::ErrorResponse { + Err(ErrorResponse { code: payment_response.status.clone().to_string(), message: payment_response.status.clone().to_string(), reason: Some(payment_response.status.to_string()), @@ -347,8 +344,8 @@ impl connector_transaction_id: Some(payment_response.id), }) } else { - Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId( + Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId( payment_response.id.clone(), ), redirection_data: None, @@ -371,7 +368,7 @@ impl Ok(Self { status, response: if is_payment_failure(status) { - Err(types::ErrorResponse { + Err(ErrorResponse { code: detailed_status .clone() .map(|volt_status| volt_status.to_string()) @@ -388,8 +385,8 @@ impl connector_transaction_id: Some(webhook_response.payment.clone()), }) } else { - Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId( + Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId( webhook_response.payment.clone(), ), redirection_data: None, @@ -447,15 +444,15 @@ pub struct RefundResponse { id: String, } -impl TryFrom> - for types::RefundsRouterData +impl TryFrom> + for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: RefundsResponseRouterData, ) -> Result { Ok(Self { - response: Ok(types::RefundsResponseData { + response: Ok(RefundsResponseData { connector_refund_id: item.response.id.to_string(), refund_status: enums::RefundStatus::Pending, //We get Refund Status only by Webhooks }), @@ -564,7 +561,7 @@ pub enum VoltDetailedStatus { AwaitingCheckoutAuthorisation, } -impl From for api::IncomingWebhookEvent { +impl From for api_models::webhooks::IncomingWebhookEvent { fn from(status: VoltWebhookBodyEventType) -> Self { match status { VoltWebhookBodyEventType::Payment(payment_data) => match payment_data.status { diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index b9ae736e435..a667c5ddc3c 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -100,8 +100,10 @@ default_imp_for_authorize_session_token!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, + connectors::Volt, connectors::Thunes, connectors::Tsys, connectors::Worldline @@ -129,6 +131,7 @@ default_imp_for_calculate_tax!( connectors::Helcim, connectors::Stax, connectors::Novalnet, + connectors::Mollie, connectors::Nexixpay, connectors::Fiuu, connectors::Globepay, @@ -136,6 +139,7 @@ default_imp_for_calculate_tax!( connectors::Powertranz, connectors::Thunes, connectors::Tsys, + connectors::Volt, connectors::Deutschebank ); @@ -161,6 +165,7 @@ default_imp_for_session_update!( connectors::Helcim, connectors::Stax, connectors::Taxjar, + connectors::Mollie, connectors::Novalnet, connectors::Nexixpay, connectors::Fiuu, @@ -169,7 +174,8 @@ default_imp_for_session_update!( connectors::Powertranz, connectors::Thunes, connectors::Tsys, - connectors::Deutschebank + connectors::Deutschebank, + connectors::Volt ); use crate::connectors; @@ -201,7 +207,8 @@ default_imp_for_complete_authorize!( connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_incremental_authorization { @@ -231,11 +238,13 @@ default_imp_for_incremental_authorization!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_create_customer { @@ -262,13 +271,15 @@ default_imp_for_create_customer!( connectors::Fiuu, connectors::Globepay, connectors::Helcim, + connectors::Mollie, connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_connector_redirect_response { @@ -302,7 +313,8 @@ default_imp_for_connector_redirect_response!( connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_pre_processing_steps{ @@ -332,11 +344,13 @@ default_imp_for_pre_processing_steps!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_post_processing_steps{ @@ -366,11 +380,13 @@ default_imp_for_post_processing_steps!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_approve { @@ -400,11 +416,13 @@ default_imp_for_approve!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_reject { @@ -434,11 +452,13 @@ default_imp_for_reject!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_webhook_source_verification { @@ -468,11 +488,13 @@ default_imp_for_webhook_source_verification!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_accept_dispute { @@ -503,11 +525,13 @@ default_imp_for_accept_dispute!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_submit_evidence { @@ -537,11 +561,13 @@ default_imp_for_submit_evidence!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_defend_dispute { @@ -571,11 +597,13 @@ default_imp_for_defend_dispute!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_file_upload { @@ -614,11 +642,13 @@ default_imp_for_file_upload!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -650,11 +680,13 @@ default_imp_for_payouts_create!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -686,11 +718,13 @@ default_imp_for_payouts_retrieve!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -722,11 +756,13 @@ default_imp_for_payouts_eligibility!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -758,11 +794,13 @@ default_imp_for_payouts_fulfill!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -794,11 +832,13 @@ default_imp_for_payouts_cancel!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -830,11 +870,13 @@ default_imp_for_payouts_quote!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -866,11 +908,13 @@ default_imp_for_payouts_recipient!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -902,11 +946,13 @@ default_imp_for_payouts_recipient_account!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -938,11 +984,13 @@ default_imp_for_frm_sale!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -974,11 +1022,13 @@ default_imp_for_frm_checkout!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -1010,11 +1060,13 @@ default_imp_for_frm_transaction!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -1046,11 +1098,13 @@ default_imp_for_frm_fulfillment!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -1082,11 +1136,13 @@ default_imp_for_frm_record_return!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_revoking_mandates { @@ -1115,9 +1171,11 @@ default_imp_for_revoking_mandates!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); diff --git a/crates/hyperswitch_connectors/src/default_implementations_v2.rs b/crates/hyperswitch_connectors/src/default_implementations_v2.rs index 82ac725de3c..3bb1abfef21 100644 --- a/crates/hyperswitch_connectors/src/default_implementations_v2.rs +++ b/crates/hyperswitch_connectors/src/default_implementations_v2.rs @@ -207,11 +207,13 @@ default_imp_for_new_connector_integration_payment!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_refund { @@ -242,11 +244,13 @@ default_imp_for_new_connector_integration_refund!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_connector_access_token { @@ -272,11 +276,13 @@ default_imp_for_new_connector_integration_connector_access_token!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_accept_dispute { @@ -308,11 +314,13 @@ default_imp_for_new_connector_integration_accept_dispute!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_submit_evidence { @@ -343,11 +351,13 @@ default_imp_for_new_connector_integration_submit_evidence!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_defend_dispute { @@ -378,11 +388,13 @@ default_imp_for_new_connector_integration_defend_dispute!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_file_upload { @@ -423,11 +435,13 @@ default_imp_for_new_connector_integration_file_upload!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -460,11 +474,13 @@ default_imp_for_new_connector_integration_payouts_create!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -497,11 +513,13 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -534,11 +552,13 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -571,11 +591,13 @@ default_imp_for_new_connector_integration_payouts_cancel!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -608,11 +630,13 @@ default_imp_for_new_connector_integration_payouts_quote!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -645,11 +669,13 @@ default_imp_for_new_connector_integration_payouts_recipient!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -682,11 +708,13 @@ default_imp_for_new_connector_integration_payouts_sync!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "payouts")] @@ -719,11 +747,13 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_webhook_source_verification { @@ -754,11 +784,13 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -791,11 +823,13 @@ default_imp_for_new_connector_integration_frm_sale!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -828,11 +862,13 @@ default_imp_for_new_connector_integration_frm_checkout!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -865,11 +901,13 @@ default_imp_for_new_connector_integration_frm_transaction!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -902,11 +940,13 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); #[cfg(feature = "frm")] @@ -939,11 +979,13 @@ default_imp_for_new_connector_integration_frm_record_return!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); macro_rules! default_imp_for_new_connector_integration_revoking_mandates { @@ -973,9 +1015,11 @@ default_imp_for_new_connector_integration_revoking_mandates!( connectors::Novalnet, connectors::Nexixpay, connectors::Powertranz, + connectors::Mollie, connectors::Stax, connectors::Taxjar, connectors::Thunes, connectors::Tsys, - connectors::Worldline + connectors::Worldline, + connectors::Volt ); diff --git a/crates/hyperswitch_connectors/src/types.rs b/crates/hyperswitch_connectors/src/types.rs index 1624b03171b..05279b53ef8 100644 --- a/crates/hyperswitch_connectors/src/types.rs +++ b/crates/hyperswitch_connectors/src/types.rs @@ -1,8 +1,9 @@ use hyperswitch_domain_models::{ - router_data::RouterData, - router_flow_types::{Capture, PSync, Void}, + router_data::{AccessToken, RouterData}, + router_flow_types::{AccessTokenAuth, Capture, PSync, Void}, router_request_types::{ - PaymentsCancelData, PaymentsCaptureData, PaymentsSyncData, RefundsData, + AccessTokenRequestData, PaymentsCancelData, PaymentsCaptureData, PaymentsSyncData, + RefundsData, }, router_response_types::{PaymentsResponseData, RefundsResponseData}, }; @@ -14,6 +15,8 @@ pub type PaymentsCaptureResponseRouterData = ResponseRouterData; pub(crate) type RefundsResponseRouterData = ResponseRouterData; +pub(crate) type RefreshTokenRouterData = + RouterData; pub type PaymentsCancelResponseRouterData = ResponseRouterData; diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index a60a0d73ff3..bc8502cdd8b 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -19,9 +19,9 @@ use hyperswitch_domain_models::{ payment_method_data::{Card, PaymentMethodData}, router_data::{PaymentMethodToken, RecurringMandatePaymentData}, router_request_types::{ - AuthenticationData, BrowserInformation, CompleteAuthorizeData, PaymentsAuthorizeData, - PaymentsCancelData, PaymentsCaptureData, PaymentsSyncData, RefundsData, ResponseId, - SetupMandateRequestData, + AuthenticationData, BrowserInformation, CompleteAuthorizeData, + PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, + PaymentsCaptureData, PaymentsSyncData, RefundsData, ResponseId, SetupMandateRequestData, }, }; use hyperswitch_interfaces::{api, errors}; @@ -1311,6 +1311,18 @@ impl PaymentsSetupMandateRequestData for SetupMandateRequestData { } } +pub trait PaymentMethodTokenizationRequestData { + fn get_browser_info(&self) -> Result; +} + +impl PaymentMethodTokenizationRequestData for PaymentMethodTokenizationData { + fn get_browser_info(&self) -> Result { + self.browser_info + .clone() + .ok_or_else(missing_field_err("browser_info")) + } +} + pub trait PaymentsCompleteAuthorizeRequestData { fn is_auto_capture(&self) -> Result; fn get_email(&self) -> Result; diff --git a/crates/hyperswitch_interfaces/Cargo.toml b/crates/hyperswitch_interfaces/Cargo.toml index b225f897871..7ab885de3a3 100644 --- a/crates/hyperswitch_interfaces/Cargo.toml +++ b/crates/hyperswitch_interfaces/Cargo.toml @@ -28,6 +28,7 @@ serde_json = "1.0.115" strum = { version = "0.26", features = ["derive"] } thiserror = "1.0.58" time = "0.3.35" +url = "2.5.0" # First party crates hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index caa5b13b51c..b505f8c8c52 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -27,7 +27,6 @@ pub mod iatapay; pub mod itaubank; pub mod klarna; pub mod mifinity; -pub mod mollie; pub mod multisafepay; pub mod netcetera; pub mod nexinets; @@ -55,7 +54,6 @@ pub mod stripe; pub mod threedsecureio; pub mod trustpay; pub mod utils; -pub mod volt; pub mod wellsfargo; pub mod wellsfargopayout; pub mod wise; @@ -66,9 +64,10 @@ pub mod zsl; pub use hyperswitch_connectors::connectors::{ bambora, bambora::Bambora, bitpay, bitpay::Bitpay, deutschebank, deutschebank::Deutschebank, fiserv, fiserv::Fiserv, fiservemea, fiservemea::Fiservemea, fiuu, fiuu::Fiuu, globepay, - globepay::Globepay, helcim, helcim::Helcim, nexixpay, nexixpay::Nexixpay, novalnet, - novalnet::Novalnet, powertranz, powertranz::Powertranz, stax, stax::Stax, taxjar, - taxjar::Taxjar, thunes, thunes::Thunes, tsys, tsys::Tsys, worldline, worldline::Worldline, + globepay::Globepay, helcim, helcim::Helcim, mollie, mollie::Mollie, nexixpay, + nexixpay::Nexixpay, novalnet, novalnet::Novalnet, powertranz, powertranz::Powertranz, stax, + stax::Stax, taxjar, taxjar::Taxjar, thunes, thunes::Thunes, tsys, tsys::Tsys, volt, volt::Volt, + worldline, worldline::Worldline, }; #[cfg(feature = "dummy_connector")] @@ -80,12 +79,12 @@ pub use self::{ cashtocode::Cashtocode, checkout::Checkout, coinbase::Coinbase, cryptopay::Cryptopay, cybersource::Cybersource, datatrans::Datatrans, dlocal::Dlocal, ebanx::Ebanx, forte::Forte, globalpay::Globalpay, gocardless::Gocardless, gpayments::Gpayments, iatapay::Iatapay, - itaubank::Itaubank, klarna::Klarna, mifinity::Mifinity, mollie::Mollie, - multisafepay::Multisafepay, netcetera::Netcetera, nexinets::Nexinets, nmi::Nmi, noon::Noon, - nuvei::Nuvei, opayo::Opayo, opennode::Opennode, paybox::Paybox, payeezy::Payeezy, payme::Payme, - payone::Payone, paypal::Paypal, payu::Payu, placetopay::Placetopay, plaid::Plaid, - prophetpay::Prophetpay, rapyd::Rapyd, razorpay::Razorpay, riskified::Riskified, shift4::Shift4, - signifyd::Signifyd, square::Square, stripe::Stripe, threedsecureio::Threedsecureio, - trustpay::Trustpay, volt::Volt, wellsfargo::Wellsfargo, wellsfargopayout::Wellsfargopayout, - wise::Wise, worldpay::Worldpay, zen::Zen, zsl::Zsl, + itaubank::Itaubank, klarna::Klarna, mifinity::Mifinity, multisafepay::Multisafepay, + netcetera::Netcetera, nexinets::Nexinets, nmi::Nmi, noon::Noon, nuvei::Nuvei, opayo::Opayo, + opennode::Opennode, paybox::Paybox, payeezy::Payeezy, payme::Payme, payone::Payone, + paypal::Paypal, payu::Payu, placetopay::Placetopay, plaid::Plaid, prophetpay::Prophetpay, + rapyd::Rapyd, razorpay::Razorpay, riskified::Riskified, shift4::Shift4, signifyd::Signifyd, + square::Square, stripe::Stripe, threedsecureio::Threedsecureio, trustpay::Trustpay, + wellsfargo::Wellsfargo, wellsfargopayout::Wellsfargopayout, wise::Wise, worldpay::Worldpay, + zen::Zen, zsl::Zsl, }; diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs index 19d67733d3b..041b5bb7359 100644 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ b/crates/router/src/core/payments/connector_integration_v2_impls.rs @@ -698,7 +698,6 @@ default_imp_for_new_connector_integration_payment!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -724,7 +723,6 @@ default_imp_for_new_connector_integration_payment!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -778,7 +776,6 @@ default_imp_for_new_connector_integration_refund!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -804,7 +801,6 @@ default_imp_for_new_connector_integration_refund!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -852,7 +848,6 @@ default_imp_for_new_connector_integration_connector_access_token!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -878,7 +873,6 @@ default_imp_for_new_connector_integration_connector_access_token!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -948,7 +942,6 @@ default_imp_for_new_connector_integration_accept_dispute!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -974,7 +967,6 @@ default_imp_for_new_connector_integration_accept_dispute!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1026,7 +1018,6 @@ default_imp_for_new_connector_integration_defend_dispute!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1052,7 +1043,6 @@ default_imp_for_new_connector_integration_defend_dispute!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1088,7 +1078,6 @@ default_imp_for_new_connector_integration_submit_evidence!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1114,7 +1103,6 @@ default_imp_for_new_connector_integration_submit_evidence!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1177,7 +1165,6 @@ default_imp_for_new_connector_integration_file_upload!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1203,7 +1190,6 @@ default_imp_for_new_connector_integration_file_upload!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1345,7 +1331,6 @@ default_imp_for_new_connector_integration_payouts_create!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1371,7 +1356,6 @@ default_imp_for_new_connector_integration_payouts_create!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1426,7 +1410,6 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1452,7 +1435,6 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1507,7 +1489,6 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1533,7 +1514,6 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1588,7 +1568,6 @@ default_imp_for_new_connector_integration_payouts_cancel!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1614,7 +1593,6 @@ default_imp_for_new_connector_integration_payouts_cancel!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1669,7 +1647,6 @@ default_imp_for_new_connector_integration_payouts_quote!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1695,7 +1672,6 @@ default_imp_for_new_connector_integration_payouts_quote!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1750,7 +1726,6 @@ default_imp_for_new_connector_integration_payouts_recipient!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1776,7 +1751,6 @@ default_imp_for_new_connector_integration_payouts_recipient!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1831,7 +1805,6 @@ default_imp_for_new_connector_integration_payouts_sync!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1857,7 +1830,6 @@ default_imp_for_new_connector_integration_payouts_sync!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1912,7 +1884,6 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1938,7 +1909,6 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -1991,7 +1961,6 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2017,7 +1986,6 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -2159,7 +2127,6 @@ default_imp_for_new_connector_integration_frm_sale!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2185,7 +2152,6 @@ default_imp_for_new_connector_integration_frm_sale!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -2240,7 +2206,6 @@ default_imp_for_new_connector_integration_frm_checkout!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2266,7 +2231,6 @@ default_imp_for_new_connector_integration_frm_checkout!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -2321,7 +2285,6 @@ default_imp_for_new_connector_integration_frm_transaction!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2347,7 +2310,6 @@ default_imp_for_new_connector_integration_frm_transaction!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -2402,7 +2364,6 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2428,7 +2389,6 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -2483,7 +2443,6 @@ default_imp_for_new_connector_integration_frm_record_return!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2509,7 +2468,6 @@ default_imp_for_new_connector_integration_frm_record_return!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, @@ -2561,7 +2519,6 @@ default_imp_for_new_connector_integration_revoking_mandates!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2587,7 +2544,6 @@ default_imp_for_new_connector_integration_revoking_mandates!( connector::Shift4, connector::Trustpay, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wise, connector::Worldpay, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 1dec8911d31..984b24ab785 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -241,7 +241,6 @@ default_imp_for_complete_authorize!( connector::Stripe, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wise, connector::Wellsfargo, connector::Wellsfargopayout, @@ -303,7 +302,6 @@ default_imp_for_webhook_source_verification!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -329,7 +327,6 @@ default_imp_for_webhook_source_verification!( connector::Stripe, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -392,7 +389,6 @@ default_imp_for_create_customer!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -418,7 +414,6 @@ default_imp_for_create_customer!( connector::Square, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -497,7 +492,6 @@ default_imp_for_connector_redirect_response!( connector::Signifyd, connector::Square, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -649,7 +643,6 @@ default_imp_for_accept_dispute!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -676,7 +669,6 @@ default_imp_for_accept_dispute!( connector::Stripe, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -760,7 +752,6 @@ default_imp_for_file_upload!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -785,7 +776,6 @@ default_imp_for_file_upload!( connector::Square, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Opennode, connector::Wellsfargo, connector::Wellsfargopayout, @@ -848,7 +838,6 @@ default_imp_for_submit_evidence!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -873,7 +862,6 @@ default_imp_for_submit_evidence!( connector::Square, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Opennode, connector::Wellsfargo, connector::Wellsfargopayout, @@ -928,15 +916,14 @@ default_imp_for_defend_dispute!( connector::Datatrans, connector::Dlocal, connector::Ebanx, - connector::Forte, connector::Globalpay, + connector::Forte, connector::Gocardless, connector::Gpayments, connector::Iatapay, connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -962,7 +949,6 @@ default_imp_for_defend_dispute!( connector::Stripe, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Opennode, connector::Wellsfargo, connector::Wellsfargopayout, @@ -1038,7 +1024,6 @@ default_imp_for_pre_processing_steps!( connector::Gpayments, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1058,7 +1043,6 @@ default_imp_for_pre_processing_steps!( connector::Signifyd, connector::Square, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -1114,7 +1098,6 @@ default_imp_for_post_processing_steps!( connector::Gpayments, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1132,7 +1115,6 @@ default_imp_for_post_processing_steps!( connector::Signifyd, connector::Square, connector::Threedsecureio, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -1278,7 +1260,6 @@ default_imp_for_payouts_create!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1303,7 +1284,6 @@ default_imp_for_payouts_create!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Worldpay, @@ -1367,7 +1347,6 @@ default_imp_for_payouts_retrieve!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1393,7 +1372,6 @@ default_imp_for_payouts_retrieve!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -1459,7 +1437,6 @@ default_imp_for_payouts_eligibility!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1486,7 +1463,6 @@ default_imp_for_payouts_eligibility!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Worldpay, @@ -1546,7 +1522,6 @@ default_imp_for_payouts_fulfill!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1570,7 +1545,6 @@ default_imp_for_payouts_fulfill!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Worldpay, @@ -1632,7 +1606,6 @@ default_imp_for_payouts_cancel!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1658,7 +1631,6 @@ default_imp_for_payouts_cancel!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Worldpay, @@ -1721,7 +1693,6 @@ default_imp_for_payouts_quote!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1748,7 +1719,6 @@ default_imp_for_payouts_quote!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Worldpay, @@ -1811,7 +1781,6 @@ default_imp_for_payouts_recipient!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1837,7 +1806,6 @@ default_imp_for_payouts_recipient!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Worldpay, @@ -1904,7 +1872,6 @@ default_imp_for_payouts_recipient_account!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -1930,7 +1897,6 @@ default_imp_for_payouts_recipient_account!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -1994,7 +1960,6 @@ default_imp_for_approve!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2021,7 +1986,6 @@ default_imp_for_approve!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2085,7 +2049,6 @@ default_imp_for_reject!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2112,7 +2075,6 @@ default_imp_for_reject!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2266,7 +2228,6 @@ default_imp_for_frm_sale!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2291,7 +2252,6 @@ default_imp_for_frm_sale!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2357,7 +2317,6 @@ default_imp_for_frm_checkout!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2382,7 +2341,6 @@ default_imp_for_frm_checkout!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2448,7 +2406,6 @@ default_imp_for_frm_transaction!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2473,7 +2430,6 @@ default_imp_for_frm_transaction!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2539,7 +2495,6 @@ default_imp_for_frm_fulfillment!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2564,7 +2519,6 @@ default_imp_for_frm_fulfillment!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2630,7 +2584,6 @@ default_imp_for_frm_record_return!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2655,7 +2608,6 @@ default_imp_for_frm_record_return!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -2718,7 +2670,6 @@ default_imp_for_incremental_authorization!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2745,7 +2696,6 @@ default_imp_for_incremental_authorization!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargopayout, connector::Wise, connector::Worldpay, @@ -2805,7 +2755,6 @@ default_imp_for_revoking_mandates!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -2831,7 +2780,6 @@ default_imp_for_revoking_mandates!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wise, connector::Worldpay, connector::Zen, @@ -3052,7 +3000,6 @@ default_imp_for_authorize_session_token!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -3077,7 +3024,6 @@ default_imp_for_authorize_session_token!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -3139,7 +3085,6 @@ default_imp_for_calculate_tax!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -3166,7 +3111,6 @@ default_imp_for_calculate_tax!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, @@ -3228,7 +3172,6 @@ default_imp_for_session_update!( connector::Itaubank, connector::Klarna, connector::Mifinity, - connector::Mollie, connector::Multisafepay, connector::Netcetera, connector::Nexinets, @@ -3255,7 +3198,6 @@ default_imp_for_session_update!( connector::Shift4, connector::Threedsecureio, connector::Trustpay, - connector::Volt, connector::Wellsfargo, connector::Wellsfargopayout, connector::Wise, From 6f2ee1d58b1aa6da408f65129be9614aff5d4b1f Mon Sep 17 00:00:00 2001 From: Gnanasundari24 <118818938+Gnanasundari24@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:54:06 +0530 Subject: [PATCH 08/48] ci(test): Remove giropay from trustpay collection (#5923) --- .../.meta.json | 7 -- .../Payments - Confirm/.event.meta.json | 5 - .../Payments - Confirm/event.test.js | 103 ------------------ .../Payments - Confirm/request.json | 77 ------------- .../Payments - Confirm/response.json | 1 - .../Payments - Create/.event.meta.json | 5 - .../Payments - Create/event.test.js | 71 ------------ .../Payments - Create/request.json | 92 ---------------- .../Payments - Create/response.json | 1 - .../Payments - Retrieve/.event.meta.json | 5 - .../Payments - Retrieve/event.test.js | 71 ------------ .../Payments - Retrieve/request.json | 34 ------ .../Payments - Retrieve/response.json | 1 - 13 files changed, 473 deletions(-) delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/.meta.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/.event.meta.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/event.test.js delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/request.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/response.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/.event.meta.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/event.test.js delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/request.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/response.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/.event.meta.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/event.test.js delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/request.json delete mode 100644 postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/response.json diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/.meta.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/.meta.json deleted file mode 100644 index 57d3f8e2bc7..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/.meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "childrenOrder": [ - "Payments - Create", - "Payments - Confirm", - "Payments - Retrieve" - ] -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/.event.meta.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/.event.meta.json deleted file mode 100644 index 688c85746ef..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/.event.meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "eventOrder": [ - "event.test.js" - ] -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/event.test.js b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/event.test.js deleted file mode 100644 index f2050e61fc3..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/event.test.js +++ /dev/null @@ -1,103 +0,0 @@ -// Validate status 2xx -pm.test("[POST]::/payments/:id/confirm - Status code is 2xx", function () { - pm.response.to.be.success; -}); - -// Validate if response header has matching content-type -pm.test( - "[POST]::/payments/:id/confirm - Content-Type is application/json", - function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", - ); - }, -); - -// Validate if response has JSON Body -pm.test("[POST]::/payments/:id/confirm - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); -}); - -// Set response object as internal variable -let jsonData = {}; -try { - jsonData = pm.response.json(); -} catch (e) {} - -// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id -if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log( - "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", - ); -} - -// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id -if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log( - "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", - ); -} - -// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret -if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log( - "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, - ); -} else { - console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", - ); -} - -// Response body should have value "requires_customer_action" for "status" -if (jsonData?.status) { - pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'requires_customer_action'", - function () { - pm.expect(jsonData.status).to.eql("requires_customer_action"); - }, - ); -} - -// Response body should have "next_action.redirect_to_url" -pm.test( - "[POST]::/payments - Content check if 'next_action.redirect_to_url' exists", - function () { - pm.expect(typeof jsonData.next_action.redirect_to_url !== "undefined").to.be - .true; - }, -); - -// Response body should have value "giropay" for "payment_method_type" -if (jsonData?.payment_method_type) { - pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'payment_method_type' matches 'giropay'", - function () { - pm.expect(jsonData.payment_method_type).to.eql("giropay"); - }, - ); -} - -// Response body should have value "stripe" for "connector" -if (jsonData?.connector) { - pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'connector' matches 'trustpay'", - function () { - pm.expect(jsonData.connector).to.eql("trustpay"); - }, - ); -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/request.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/request.json deleted file mode 100644 index 3f8a95e59bb..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/request.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "value", - "value": "{{publishable_key}}", - "type": "string" - }, - { - "key": "key", - "value": "api-key", - "type": "string" - }, - { - "key": "in", - "value": "header", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw_json_formatted": { - "client_secret": "{{client_secret}}", - "payment_method": "bank_redirect", - "payment_method_type": "giropay", - "payment_method_data": { - "bank_redirect": { - "giropay": { - "billing_details": { - "billing_name": "John Doe" - }, - "bank_name": "", - "preferred_language": "en", - "country": "DE" - } - } - } - } - }, - "url": { - "raw": "{{baseUrl}}/payments/:id/confirm", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id", - "confirm" - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/response.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/response.json deleted file mode 100644 index fe51488c706..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Confirm/response.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/.event.meta.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/.event.meta.json deleted file mode 100644 index 688c85746ef..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/.event.meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "eventOrder": [ - "event.test.js" - ] -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/event.test.js b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/event.test.js deleted file mode 100644 index 0444324000a..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/event.test.js +++ /dev/null @@ -1,71 +0,0 @@ -// Validate status 2xx -pm.test("[POST]::/payments - Status code is 2xx", function () { - pm.response.to.be.success; -}); - -// Validate if response header has matching content-type -pm.test("[POST]::/payments - Content-Type is application/json", function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", - ); -}); - -// Validate if response has JSON Body -pm.test("[POST]::/payments - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); -}); - -// Set response object as internal variable -let jsonData = {}; -try { - jsonData = pm.response.json(); -} catch (e) {} - -// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id -if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log( - "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", - ); -} - -// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id -if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log( - "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", - ); -} - -// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret -if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log( - "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, - ); -} else { - console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", - ); -} - -// Response body should have value "requires_payment_method" for "status" -if (jsonData?.status) { - pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'", - function () { - pm.expect(jsonData.status).to.eql("requires_payment_method"); - }, - ); -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/request.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/request.json deleted file mode 100644 index 87d07a6016b..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/request.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw_json_formatted": { - "amount": 1000, - "currency": "EUR", - "confirm": false, - "capture_method": "automatic", - "capture_on": "2022-09-10T10:11:12Z", - "amount_to_capture": 1000, - "customer_id": "StripeCustomer", - "email": "abcdef123@gmail.com", - "name": "John Doe", - "phone": "999999999", - "phone_country_code": "+65", - "description": "Its my first payment request", - "authentication_type": "three_ds", - "return_url": "https://duck.com", - "billing": { - "address": { - "first_name": "John", - "last_name": "Doe", - "line1": "1467", - "line2": "Harrison Street", - "line3": "Harrison Street", - "city": "San Fransico", - "state": "California", - "zip": "94122", - "country": "DE" - } - }, - "browser_info": { - "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", - "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", - "language": "nl-NL", - "color_depth": 24, - "screen_height": 723, - "screen_width": 1536, - "time_zone": 0, - "java_enabled": true, - "java_script_enabled": true, - "ip_address": "127.0.0.1" - }, - "shipping": { - "address": { - "line1": "1467", - "line2": "Harrison Street", - "line3": "Harrison Street", - "city": "San Fransico", - "state": "California", - "zip": "94122", - "country": "US", - "first_name": "John", - "last_name": "Doe" - } - }, - "statement_descriptor_name": "joseph", - "statement_descriptor_suffix": "JS", - "metadata": { - "udf1": "value1", - "new_customer": "true", - "login_date": "2019-09-10T10:11:12Z" - } - } - }, - "url": { - "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] - }, - "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/response.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/response.json deleted file mode 100644 index fe51488c706..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Create/response.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/.event.meta.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/.event.meta.json deleted file mode 100644 index 688c85746ef..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/.event.meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "eventOrder": [ - "event.test.js" - ] -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/event.test.js b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/event.test.js deleted file mode 100644 index 9053ddab13b..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/event.test.js +++ /dev/null @@ -1,71 +0,0 @@ -// Validate status 2xx -pm.test("[GET]::/payments/:id - Status code is 2xx", function () { - pm.response.to.be.success; -}); - -// Validate if response header has matching content-type -pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", - ); -}); - -// Validate if response has JSON Body -pm.test("[GET]::/payments/:id - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); -}); - -// Set response object as internal variable -let jsonData = {}; -try { - jsonData = pm.response.json(); -} catch (e) {} - -// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id -if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log( - "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", - ); -} - -// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id -if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log( - "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", - ); -} - -// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret -if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log( - "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, - ); -} else { - console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", - ); -} - -// Response body should have value "requires_customer_action" for "status" -if (jsonData?.status) { - pm.test( - "[POST]::/payments:id - Content check if value for 'status' matches 'requires_customer_action'", - function () { - pm.expect(jsonData.status).to.eql("requires_customer_action"); - }, - ); -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/request.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/request.json deleted file mode 100644 index 6f4d51c5945..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/request.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/payments/:id", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id" - ], - "query": [ - { - "key": "force_sync", - "value": "true", - "disabled": true - } - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" -} diff --git a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/response.json b/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/response.json deleted file mode 100644 index fe51488c706..00000000000 --- a/postman/collection-dir/trustpay/Flow Testcases/Happy Cases/Scenario8-Bank Redirect-giropay/Payments - Retrieve/response.json +++ /dev/null @@ -1 +0,0 @@ -[] From d7d6a3101611857319d9f5fb607f7df7d3fd9d59 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 00:19:55 +0000 Subject: [PATCH 09/48] chore(postman): update Postman collection files --- .../trustpay.postman_collection.json | 422 ------------------ 1 file changed, 422 deletions(-) diff --git a/postman/collection-json/trustpay.postman_collection.json b/postman/collection-json/trustpay.postman_collection.json index 78033cce6ee..923e40a677d 100644 --- a/postman/collection-json/trustpay.postman_collection.json +++ b/postman/collection-json/trustpay.postman_collection.json @@ -3493,428 +3493,6 @@ } ] }, - { - "name": "Scenario8-Bank Redirect-giropay", - "item": [ - { - "name": "Payments - Create", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"requires_payment_method\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_payment_method\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"DE\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" - }, - "url": { - "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] - }, - "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" - }, - "response": [] - }, - { - "name": "Payments - Confirm", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[POST]::/payments/:id/confirm - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(", - " \"[POST]::/payments/:id/confirm - Content-Type is application/json\",", - " function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - " },", - ");", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/payments/:id/confirm - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"requires_customer_action\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'requires_customer_action'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", - " },", - " );", - "}", - "", - "// Response body should have \"next_action.redirect_to_url\"", - "pm.test(", - " \"[POST]::/payments - Content check if 'next_action.redirect_to_url' exists\",", - " function () {", - " pm.expect(typeof jsonData.next_action.redirect_to_url !== \"undefined\").to.be", - " .true;", - " },", - ");", - "", - "// Response body should have value \"giropay\" for \"payment_method_type\"", - "if (jsonData?.payment_method_type) {", - " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'payment_method_type' matches 'giropay'\",", - " function () {", - " pm.expect(jsonData.payment_method_type).to.eql(\"giropay\");", - " },", - " );", - "}", - "", - "// Response body should have value \"stripe\" for \"connector\"", - "if (jsonData?.connector) {", - " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'connector' matches 'trustpay'\",", - " function () {", - " pm.expect(jsonData.connector).to.eql(\"trustpay\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "value", - "value": "{{publishable_key}}", - "type": "string" - }, - { - "key": "key", - "value": "api-key", - "type": "string" - }, - { - "key": "in", - "value": "header", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"giropay\",\"payment_method_data\":{\"bank_redirect\":{\"giropay\":{\"billing_details\":{\"billing_name\":\"John Doe\"},\"bank_name\":\"\",\"preferred_language\":\"en\",\"country\":\"DE\"}}}}" - }, - "url": { - "raw": "{{baseUrl}}/payments/:id/confirm", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id", - "confirm" - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" - }, - "response": [] - }, - { - "name": "Payments - Retrieve", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"requires_customer_action\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments:id - Content check if value for 'status' matches 'requires_customer_action'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/payments/:id", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id" - ], - "query": [ - { - "key": "force_sync", - "value": "true", - "disabled": true - } - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" - }, - "response": [] - } - ] - }, { "name": "Scenario1-Create payment with confirm true", "item": [ From 04420040249482581d3666eaa41163aa26ea47f2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 00:24:09 +0000 Subject: [PATCH 10/48] chore(version): 2024.09.18.0 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16c9a8c8e0..8b0420e2e2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,33 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.09.18.0 + +### Features + +- **router:** Add admin list apis for v2 ([#5883](https://github.com/juspay/hyperswitch/pull/5883)) ([`bc6c460`](https://github.com/juspay/hyperswitch/commit/bc6c460ca9767d3b80604166ce2cbd98550d175a)) + +### Bug Fixes + +- **connector:** Add field types for bank debits ([#5908](https://github.com/juspay/hyperswitch/pull/5908)) ([`90e8de1`](https://github.com/juspay/hyperswitch/commit/90e8de18a854a7f83470e84a954ed365078c24c2)) +- **frm:** Add feature flag for querying FRM data ([#5889](https://github.com/juspay/hyperswitch/pull/5889)) ([`75400a3`](https://github.com/juspay/hyperswitch/commit/75400a3af7aaff87e03dbab4e00be8b00ed5a70e)) +- **router:** + - Cache invalidation in merchant_connector_update flow for v2 ([#5918](https://github.com/juspay/hyperswitch/pull/5918)) ([`66de680`](https://github.com/juspay/hyperswitch/commit/66de680e6fcaa318694df48dafdaabfceb745961)) + - [stripe] remove passing of customer_acceptance from Mandate Payment Request ([#5920](https://github.com/juspay/hyperswitch/pull/5920)) ([`97c8e98`](https://github.com/juspay/hyperswitch/commit/97c8e98a48f514b0ba36b109971dd479ae84861e)) + +### Refactors + +- **connector:** Move connector Volt and Mollie from Router to HyperswitchConnector Trait ([#5612](https://github.com/juspay/hyperswitch/pull/5612)) ([`1d9e639`](https://github.com/juspay/hyperswitch/commit/1d9e6396522fc0b6cbe0b7ff1be2377098655690)) +- Add encryption support to payment attempt domain model ([#5882](https://github.com/juspay/hyperswitch/pull/5882)) ([`f72abe4`](https://github.com/juspay/hyperswitch/commit/f72abe4b979873b06d75553c7412f8072c29c8a9)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`d7d6a31`](https://github.com/juspay/hyperswitch/commit/d7d6a3101611857319d9f5fb607f7df7d3fd9d59)) + +**Full Changelog:** [`2024.09.17.0...2024.09.18.0`](https://github.com/juspay/hyperswitch/compare/2024.09.17.0...2024.09.18.0) + +- - - + ## 2024.09.17.0 ### Features From be902ffa5328d32efe70c40c36f86d8fbfa01c79 Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:22:09 +0530 Subject: [PATCH 11/48] feat(payment_methods_v2): Payment method Create API (#5812) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/Cargo.toml | 2 +- crates/api_models/src/events.rs | 18 + crates/api_models/src/payment_methods.rs | 103 +- crates/common_utils/Cargo.toml | 1 + crates/common_utils/src/events.rs | 2 + crates/common_utils/src/id_type.rs | 4 + crates/common_utils/src/id_type/global_id.rs | 2 + .../src/id_type/payment_methods.rs | 85 ++ crates/diesel_models/src/payment_method.rs | 8 +- .../diesel_models/src/query/payment_method.rs | 5 +- .../src/payment_methods.rs | 4 +- crates/router/Cargo.toml | 2 +- crates/router/src/consts.rs | 12 + crates/router/src/core/errors.rs | 6 + crates/router/src/core/payment_methods.rs | 1005 ++++++++++++++++- .../router/src/core/payment_methods/cards.rs | 750 +++--------- .../src/core/payment_methods/transformers.rs | 132 ++- crates/router/src/core/payments.rs | 39 + crates/router/src/core/payments/helpers.rs | 67 ++ .../payments/operations/payment_create.rs | 29 + .../payments/operations/payment_response.rs | 37 + .../payments/operations/payment_status.rs | 22 + crates/router/src/core/pm_auth.rs | 11 + crates/router/src/db/kafka_store.rs | 17 + crates/router/src/db/payment_method.rs | 169 +-- crates/router/src/routes/app.rs | 18 + crates/router/src/routes/payment_methods.rs | 121 +- crates/router/src/types.rs | 1 + crates/router/src/types/api/mandates.rs | 70 +- .../router/src/types/api/payment_methods.rs | 72 +- crates/router/src/types/payment_methods.rs | 103 ++ .../src/types/storage/payment_method.rs | 29 + crates/router/src/types/transformers.rs | 14 +- .../workflows/payment_method_status_update.rs | 4 + crates/storage_impl/src/lib.rs | 2 +- 35 files changed, 2168 insertions(+), 798 deletions(-) create mode 100644 crates/common_utils/src/id_type/payment_methods.rs create mode 100644 crates/router/src/types/payment_methods.rs diff --git a/crates/api_models/Cargo.toml b/crates/api_models/Cargo.toml index 00c12b9255b..c16b1d9a418 100644 --- a/crates/api_models/Cargo.toml +++ b/crates/api_models/Cargo.toml @@ -20,7 +20,7 @@ v1 = ["common_utils/v1"] v2 = ["common_utils/v2", "customer_v2"] customer_v2 = ["common_utils/customer_v2"] payment_v2 = [] -payment_methods_v2 = [] +payment_methods_v2 = ["common_utils/payment_methods_v2"] [dependencies] actix-web = { version = "4.5.1", optional = true } diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 58727a46cf3..13966c464d5 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -152,3 +152,21 @@ impl ApiEventMetric for MetricsResponse { Some(ApiEventsType::Miscellaneous) } } + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl ApiEventMetric for PaymentMethodIntentConfirmInternal { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethod { + payment_method_id: self.id.clone(), + payment_method: Some(self.payment_method), + payment_method_type: Some(self.payment_method_type), + }) + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl ApiEventMetric for PaymentMethodIntentCreate { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethodCreate) + } +} diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index c6cf6380150..6c93b0bb5bd 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -160,8 +160,68 @@ pub struct PaymentMethodIntentConfirm { /// Payment method data to be passed pub payment_method_data: PaymentMethodCreateData, + + /// The type of payment method use for the payment. + #[schema(value_type = PaymentMethod,example = "card")] + pub payment_method: api_enums::PaymentMethod, + + /// This is a sub-category of payment method. + #[schema(value_type = PaymentMethodType,example = "credit")] + pub payment_method_type: api_enums::PaymentMethodType, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl PaymentMethodIntentConfirm { + pub fn validate_payment_method_data_against_payment_method( + payment_method: api_enums::PaymentMethod, + payment_method_data: PaymentMethodCreateData, + ) -> bool { + match payment_method { + api_enums::PaymentMethod::Card => { + matches!(payment_method_data, PaymentMethodCreateData::Card(_)) + } + _ => false, + } + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] +#[serde(deny_unknown_fields)] +pub struct PaymentMethodIntentConfirmInternal { + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub id: String, + /// The type of payment method use for the payment. + #[schema(value_type = PaymentMethod,example = "card")] + pub payment_method: api_enums::PaymentMethod, + + /// This is a sub-category of payment method. + #[schema(value_type = PaymentMethodType,example = "credit")] + pub payment_method_type: api_enums::PaymentMethodType, + + /// For SDK based calls, client_secret would be required + pub client_secret: String, + + /// The unique identifier of the customer. + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, + + /// Payment method data to be passed + pub payment_method_data: PaymentMethodCreateData, } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl From for PaymentMethodIntentConfirm { + fn from(item: PaymentMethodIntentConfirmInternal) -> Self { + Self { + client_secret: item.client_secret, + payment_method: item.payment_method, + payment_method_type: item.payment_method_type, + customer_id: item.customer_id, + payment_method_data: item.payment_method_data.clone(), + } + } +} #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] /// This struct is only used by and internal api to migrate payment method pub struct PaymentMethodMigrate { @@ -276,11 +336,16 @@ impl PaymentMethodCreate { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl PaymentMethodCreate { - pub fn get_payment_method_create_from_payment_method_migrate( - _card_number: CardNumber, - _payment_method_migrate: &PaymentMethodMigrate, - ) -> Self { - todo!() + pub fn validate_payment_method_data_against_payment_method( + payment_method: api_enums::PaymentMethod, + payment_method_data: PaymentMethodCreateData, + ) -> bool { + match payment_method { + api_enums::PaymentMethod::Card => { + matches!(payment_method_data, PaymentMethodCreateData::Card(_)) + } + _ => false, + } } } @@ -332,12 +397,6 @@ pub enum PaymentMethodUpdateData { #[serde(rename = "payment_method_data")] pub enum PaymentMethodCreateData { Card(CardDetail), - #[cfg(feature = "payouts")] - #[schema(value_type = Bank)] - BankTransfer(payouts::Bank), - #[cfg(feature = "payouts")] - #[schema(value_type = Wallet)] - Wallet(payouts::Wallet), } #[cfg(all( @@ -577,9 +636,6 @@ impl CardDetailUpdate { #[serde(rename = "payment_method_data")] pub enum PaymentMethodResponseData { Card(CardDetailFromLocker), - #[cfg(feature = "payouts")] - #[schema(value_type = Bank)] - Bank(payouts::Bank), } #[cfg(all( @@ -934,6 +990,25 @@ impl From for CardDetailFromLocker { } } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl From for CardDetailsPaymentMethod { + fn from(item: CardDetail) -> Self { + Self { + issuer_country: item.card_issuing_country.map(|c| c.to_string()), + last4_digits: Some(item.card_number.get_last4()), + expiry_month: Some(item.card_exp_month), + expiry_year: Some(item.card_exp_year), + card_holder_name: item.card_holder_name, + nick_name: item.nick_name, + card_isin: None, + card_issuer: item.card_issuer, + card_network: item.card_network, + card_type: item.card_type.map(|card| card.to_string()), + saved_to_locker: true, + } + } +} + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index c6e29801b2f..fdc9670045e 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -21,6 +21,7 @@ payouts = ["common_enums/payouts"] v1 = [] v2 = [] customer_v2 = [] +payment_methods_v2 = [] [dependencies] async-trait = { version = "0.1.79", optional = true } diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index 1e752a0f9b9..16d0fd9b30a 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -27,6 +27,8 @@ pub enum ApiEventsType { payment_method: Option, payment_method_type: Option, }, + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + PaymentMethodCreate, #[cfg(all(feature = "v2", feature = "customer_v2"))] Customer { id: String, diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index 14738811835..20dc423310a 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -12,6 +12,8 @@ mod profile; mod routing; mod global_id; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +mod payment_methods; pub use customer::CustomerId; use diesel::{ @@ -25,6 +27,8 @@ pub use merchant::MerchantId; pub use merchant_connector_account::MerchantConnectorAccountId; pub use organization::OrganizationId; pub use payment::PaymentId; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub use payment_methods::GlobalPaymentMethodId; pub use profile::ProfileId; pub use routing::RoutingId; use serde::{Deserialize, Serialize}; diff --git a/crates/common_utils/src/id_type/global_id.rs b/crates/common_utils/src/id_type/global_id.rs index f04bfc3f92b..8efbc3636f9 100644 --- a/crates/common_utils/src/id_type/global_id.rs +++ b/crates/common_utils/src/id_type/global_id.rs @@ -21,6 +21,7 @@ pub(crate) struct GlobalId(LengthId) pub(crate) enum GlobalEntity { Customer, Payment, + PaymentMethod, } impl GlobalEntity { @@ -28,6 +29,7 @@ impl GlobalEntity { match self { Self::Customer => "cus", Self::Payment => "pay", + Self::PaymentMethod => "pm", } } } diff --git a/crates/common_utils/src/id_type/payment_methods.rs b/crates/common_utils/src/id_type/payment_methods.rs new file mode 100644 index 00000000000..f5cc2150913 --- /dev/null +++ b/crates/common_utils/src/id_type/payment_methods.rs @@ -0,0 +1,85 @@ +use diesel::{backend::Backend, deserialize::FromSql, serialize::ToSql, sql_types}; +use error_stack::ResultExt; + +use crate::{ + errors, + errors::CustomResult, + id_type::global_id::{CellId, GlobalEntity, GlobalId, GlobalIdError}, +}; + +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, + diesel::expression::AsExpression, +)] +#[diesel(sql_type = diesel::sql_types::Text)] +pub struct GlobalPaymentMethodId(GlobalId); + +#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] +pub enum GlobalPaymentMethodIdError { + #[error("Failed to construct GlobalPaymentMethodId")] + ConstructionError, +} + +impl GlobalPaymentMethodId { + fn get_global_id(&self) -> &GlobalId { + &self.0 + } + /// Create a new GlobalPaymentMethodId from celll id information + pub fn generate(cell_id: &str) -> error_stack::Result { + let cell_id = CellId::from_string(cell_id.to_string())?; + let global_id = GlobalId::generate(cell_id, GlobalEntity::PaymentMethod); + Ok(Self(global_id)) + } + + pub fn get_string_repr(&self) -> String { + todo!() + } + + pub fn generate_from_string(value: String) -> CustomResult { + let id = GlobalId::from_string(value) + .change_context(GlobalPaymentMethodIdError::ConstructionError)?; + Ok(Self(id)) + } +} + +impl diesel::Queryable for GlobalPaymentMethodId +where + DB: diesel::backend::Backend, + Self: diesel::deserialize::FromSql, +{ + type Row = Self; + fn build(row: Self::Row) -> diesel::deserialize::Result { + Ok(row) + } +} + +impl ToSql for GlobalPaymentMethodId +where + DB: Backend, + GlobalId: ToSql, +{ + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, DB>, + ) -> diesel::serialize::Result { + let id = self.get_global_id(); + id.to_sql(out) + } +} + +impl FromSql for GlobalPaymentMethodId +where + DB: Backend, + GlobalId: FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + let global_id = GlobalId::from_sql(value)?; + Ok(Self(global_id)) + } +} diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index ca919eb15da..3ad58e62dcf 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -88,7 +88,7 @@ pub struct PaymentMethod { pub payment_method_billing_address: Option, pub updated_by: Option, pub locker_fingerprint_id: Option, - pub id: String, + pub id: common_utils::id_type::GlobalPaymentMethodId, pub version: common_enums::ApiVersion, pub network_token_requestor_reference_id: Option, pub network_token_locker_id: Option, @@ -105,7 +105,7 @@ impl PaymentMethod { } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] - pub fn get_id(&self) -> &String { + pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId { &self.id } } @@ -179,7 +179,7 @@ pub struct PaymentMethodNew { pub payment_method_billing_address: Option, pub updated_by: Option, pub locker_fingerprint_id: Option, - pub id: String, + pub id: common_utils::id_type::GlobalPaymentMethodId, pub version: common_enums::ApiVersion, pub network_token_requestor_reference_id: Option, pub network_token_locker_id: Option, @@ -200,7 +200,7 @@ impl PaymentMethodNew { } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] - pub fn get_id(&self) -> &String { + pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId { &self.id } } diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs index 37c41c19dd9..321c7a6829c 100644 --- a/crates/diesel_models/src/query/payment_method.rs +++ b/crates/diesel_models/src/query/payment_method.rs @@ -211,7 +211,10 @@ impl PaymentMethod { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl PaymentMethod { - pub async fn find_by_id(conn: &PgPooledConn, id: &str) -> StorageResult { + pub async fn find_by_id( + conn: &PgPooledConn, + id: &common_utils::id_type::GlobalPaymentMethodId, + ) -> StorageResult { generics::generic_find_one::<::Table, _, _>(conn, pm_id.eq(id.to_owned())) .await } diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index e7cbae9e88c..af3c657c34a 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -77,7 +77,7 @@ pub struct PaymentMethod { pub payment_method_billing_address: OptionalEncryptableValue, pub updated_by: Option, pub locker_fingerprint_id: Option, - pub id: String, + pub id: common_utils::id_type::GlobalPaymentMethodId, pub version: common_enums::ApiVersion, pub network_token_requestor_reference_id: Option, pub network_token_locker_id: Option, @@ -94,7 +94,7 @@ impl PaymentMethod { } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] - pub fn get_id(&self) -> &String { + pub fn get_id(&self) -> &common_utils::id_type::GlobalPaymentMethodId { &self.id } } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 0c6e06cc42d..bae6243d2e6 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -37,7 +37,7 @@ v2 = ["customer_v2", "payment_methods_v2", "payment_v2", "common_default", "api_ v1 = ["common_default", "api_models/v1", "diesel_models/v1", "hyperswitch_domain_models/v1", "storage_impl/v1", "hyperswitch_interfaces/v1", "kgraph_utils/v1"] customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"] payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2", "storage_impl/payment_v2"] -payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2"] +payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"] # Partial Auth # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request. diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 3d4a29e0f30..1f4fd00bbc5 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -135,3 +135,15 @@ pub const DEFAULT_UNIFIED_ERROR_MESSAGE: &str = "Something went wrong"; // Recon's feature tag pub const RECON_FEATURE_TAG: &str = "RECONCILIATION AND SETTLEMENT"; + +/// Vault Add request url +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub const ADD_VAULT_REQUEST_URL: &str = "/vault/add"; + +/// Vault Get Fingerprint request url +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub const VAULT_FINGERPRINT_REQUEST_URL: &str = "/fingerprint"; + +/// Vault Header content type +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub const VAULT_HEADER_CONTENT_TYPE: &str = "application/json"; diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index d8a28ebdf2e..c56bfaca913 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -148,6 +148,12 @@ pub enum VaultError { SavePaymentMethodFailed, #[error("Failed to generate fingerprint")] GenerateFingerprintFailed, + #[error("Failed to encrypt vault request")] + RequestEncryptionFailed, + #[error("Failed to decrypt vault response")] + ResponseDecryptionFailed, + #[error("Failed to call vault")] + VaultAPIError, #[error("Failed while calling locker API")] ApiError, } diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 72fbc3d3d4e..fa7ddfce997 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -21,9 +21,16 @@ use api_models::payment_methods; pub use api_models::{enums::PayoutConnectors, payouts as payout_types}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use common_utils::ext_traits::Encode; -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -use common_utils::ext_traits::OptionExt; use common_utils::{consts::DEFAULT_LOCALE, id_type::CustomerId}; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use common_utils::{ + crypto::{self, Encryptable}, + ext_traits::{AsyncExt, Encode, ValueExt}, + fp_utils::when, + generate_id, id_type, + request::RequestContent, + types as util_types, +}; use diesel_models::{ enums, GenericLinkNew, PaymentMethodCollectLink, PaymentMethodCollectLinkData, }; @@ -31,7 +38,11 @@ use error_stack::{report, ResultExt}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData}; use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use masking::ExposeInterface; use masking::PeekInterface; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use masking::Secret; use router_env::{instrument, tracing}; use time::Duration; @@ -39,11 +50,25 @@ use super::{ errors::{RouterResponse, StorageErrorExt}, pm_auth, }; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use crate::{ + configs::settings, + core::{payment_methods::transformers as pm_transforms, utils as core_utils}, + headers, logger, + routes::payment_methods as pm_routes, + services::encryption, + types::{ + api::{self, payment_methods::PaymentMethodCreateExt}, + payment_methods as pm_types, + storage::PaymentMethodListContext, + }, + utils::ext_traits::OptionExt, +}; use crate::{ consts, core::{ errors::{self, RouterResult}, - payments::helpers, + payments::helpers as payment_helpers, }, routes::{app::StorageInterface, SessionState}, services, @@ -67,7 +92,7 @@ pub async fn retrieve_payment_method( ) -> RouterResult<(Option, Option)> { match pm_data { pm_opt @ Some(pm @ domain::PaymentMethodData::Card(_)) => { - let payment_token = helpers::store_payment_method_data_in_vault( + let payment_token = payment_helpers::store_payment_method_data_in_vault( state, payment_attempt, payment_intent, @@ -81,7 +106,7 @@ pub async fn retrieve_payment_method( Ok((pm_opt.to_owned(), payment_token)) } pm_opt @ Some(pm @ domain::PaymentMethodData::BankDebit(_)) => { - let payment_token = helpers::store_payment_method_data_in_vault( + let payment_token = payment_helpers::store_payment_method_data_in_vault( state, payment_attempt, payment_intent, @@ -104,7 +129,7 @@ pub async fn retrieve_payment_method( pm @ Some(domain::PaymentMethodData::GiftCard(_)) => Ok((pm.to_owned(), None)), pm @ Some(domain::PaymentMethodData::OpenBanking(_)) => Ok((pm.to_owned(), None)), pm_opt @ Some(pm @ domain::PaymentMethodData::BankTransfer(_)) => { - let payment_token = helpers::store_payment_method_data_in_vault( + let payment_token = payment_helpers::store_payment_method_data_in_vault( state, payment_attempt, payment_intent, @@ -118,7 +143,7 @@ pub async fn retrieve_payment_method( Ok((pm_opt.to_owned(), payment_token)) } pm_opt @ Some(pm @ domain::PaymentMethodData::Wallet(_)) => { - let payment_token = helpers::store_payment_method_data_in_vault( + let payment_token = payment_helpers::store_payment_method_data_in_vault( state, payment_attempt, payment_intent, @@ -132,7 +157,7 @@ pub async fn retrieve_payment_method( Ok((pm_opt.to_owned(), payment_token)) } pm_opt @ Some(pm @ domain::PaymentMethodData::BankRedirect(_)) => { - let payment_token = helpers::store_payment_method_data_in_vault( + let payment_token = payment_helpers::store_payment_method_data_in_vault( state, payment_attempt, payment_intent, @@ -414,6 +439,10 @@ fn generate_task_id_for_payment_method_status_update_workflow( format!("{runner}_{task}_{key_id}") } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] pub async fn add_payment_method_status_update_task( db: &dyn StorageInterface, payment_method: &domain::PaymentMethod, @@ -483,7 +512,10 @@ pub async fn retrieve_payment_method_with_token( todo!() } -#[cfg(feature = "v1")] +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn retrieve_payment_method_with_token( @@ -500,7 +532,7 @@ pub async fn retrieve_payment_method_with_token( ) -> RouterResult { let token = match token_data { storage::PaymentTokenData::TemporaryGeneric(generic_token) => { - helpers::retrieve_payment_method_with_temporary_token( + payment_helpers::retrieve_payment_method_with_temporary_token( state, &generic_token.token, payment_intent, @@ -519,7 +551,7 @@ pub async fn retrieve_payment_method_with_token( } storage::PaymentTokenData::Temporary(generic_token) => { - helpers::retrieve_payment_method_with_temporary_token( + payment_helpers::retrieve_payment_method_with_temporary_token( state, &generic_token.token, payment_intent, @@ -538,7 +570,7 @@ pub async fn retrieve_payment_method_with_token( } storage::PaymentTokenData::Permanent(card_token) => { - helpers::retrieve_card_with_permanent_token( + payment_helpers::retrieve_card_with_permanent_token( state, card_token.locker_id.as_ref().unwrap_or(&card_token.token), card_token @@ -572,7 +604,7 @@ pub async fn retrieve_payment_method_with_token( } storage::PaymentTokenData::PermanentCard(card_token) => { - helpers::retrieve_card_with_permanent_token( + payment_helpers::retrieve_card_with_permanent_token( state, card_token.locker_id.as_ref().unwrap_or(&card_token.token), card_token @@ -793,3 +825,950 @@ pub(crate) async fn get_payment_method_create_request( .attach_printable("PaymentMethodData required Or Card is already saved")), } } + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +pub async fn create_payment_method( + state: &SessionState, + req: api::PaymentMethodCreate, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, +) -> errors::RouterResponse { + req.validate()?; + + let db = &*state.store; + let merchant_id = merchant_account.get_id(); + let customer_id = req.customer_id.to_owned(); + + db.find_customer_by_merchant_reference_id_merchant_id( + &(state.into()), + &customer_id, + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; + + let payment_method_billing_address: Option>> = req + .billing + .clone() + .async_map(|billing| cards::create_encrypted_data(state, key_store, billing)) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt Payment method billing address")?; + + // create pm + let payment_method_id = + common_utils::id_type::GlobalPaymentMethodId::generate("random_cell_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to generate GlobalPaymentMethodId")?; + + let payment_method = create_payment_method_for_intent( + state, + req.metadata.clone(), + &customer_id, + payment_method_id, + merchant_id, + key_store, + merchant_account.storage_scheme, + payment_method_billing_address.map(Into::into), + ) + .await + .attach_printable("Failed to add Payment method to DB")?; + + let vaulting_result = + vault_payment_method(state, &req.payment_method_data, merchant_account, key_store).await; + + let response = match vaulting_result { + Ok(resp) => { + let pm_update = create_pm_additional_data_update( + &req.payment_method_data, + state, + key_store, + Some(resp.vault_id), + Some(req.payment_method), + Some(req.payment_method_type), + ) + .await + .attach_printable("Unable to create Payment method data")?; + + let payment_method = db + .update_payment_method( + &(state.into()), + &key_store, + payment_method, + pm_update, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to update payment method in db")?; + + let resp = pm_transforms::generate_payment_method_response(&payment_method)?; + + Ok(resp) + } + Err(e) => { + let pm_update = storage::PaymentMethodUpdate::StatusUpdate { + status: Some(enums::PaymentMethodStatus::Inactive), + }; + + db.update_payment_method( + &(state.into()), + &key_store, + payment_method, + pm_update, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to update payment method in db")?; + + Err(e) + } + }?; + + Ok(services::ApplicationResponse::Json(response)) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +pub async fn payment_method_intent_create( + state: &SessionState, + req: api::PaymentMethodIntentCreate, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, +) -> errors::RouterResponse { + let db = &*state.store; + let merchant_id = merchant_account.get_id(); + let customer_id = req.customer_id.to_owned(); + + db.find_customer_by_merchant_reference_id_merchant_id( + &(state.into()), + &customer_id, + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; + + let payment_method_billing_address: Option>> = req + .billing + .clone() + .async_map(|billing| cards::create_encrypted_data(state, key_store, billing)) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt Payment method billing address")?; + + // create pm entry + + let payment_method_id = + common_utils::id_type::GlobalPaymentMethodId::generate("random_cell_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to generate GlobalPaymentMethodId")?; + + let payment_method = create_payment_method_for_intent( + state, + req.metadata.clone(), + &customer_id, + payment_method_id, + merchant_id, + key_store, + merchant_account.storage_scheme, + payment_method_billing_address.map(Into::into), + ) + .await + .attach_printable("Failed to add Payment method to DB")?; + + let resp = pm_transforms::generate_payment_method_response(&payment_method)?; + + Ok(services::ApplicationResponse::Json(resp)) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +pub async fn payment_method_intent_confirm( + state: &SessionState, + req: api::PaymentMethodIntentConfirm, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + pm_id: String, +) -> errors::RouterResponse { + req.validate()?; + + let db = &*state.store; + let client_secret = req.client_secret.clone(); + let pm_id = common_utils::id_type::GlobalPaymentMethodId::generate_from_string(pm_id) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to generate GlobalPaymentMethodId")?; + + let payment_method = db + .find_payment_method( + &(state.into()), + &key_store, + &pm_id, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) + .attach_printable("Unable to find payment method")?; + + when( + cards::authenticate_pm_client_secret_and_check_expiry(&client_secret, &payment_method)?, + || Err(errors::ApiErrorResponse::ClientSecretExpired), + )?; + + when( + payment_method.status != enums::PaymentMethodStatus::AwaitingData, + || { + Err(errors::ApiErrorResponse::InvalidRequestData { + message: "Invalid pm_id provided: This Payment method cannot be confirmed" + .to_string(), + }) + }, + )?; + + let customer_id = payment_method.customer_id.to_owned(); + db.find_customer_by_merchant_reference_id_merchant_id( + &(state.into()), + &customer_id, + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; + + let vaulting_result = + vault_payment_method(state, &req.payment_method_data, merchant_account, key_store).await; + + let response = match vaulting_result { + Ok(resp) => { + let pm_update = create_pm_additional_data_update( + &req.payment_method_data, + state, + key_store, + Some(resp.vault_id), + Some(req.payment_method), + Some(req.payment_method_type), + ) + .await + .attach_printable("Unable to create Payment method data")?; + + let payment_method = db + .update_payment_method( + &(state.into()), + &key_store, + payment_method, + pm_update, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to update payment method in db")?; + + let resp = pm_transforms::generate_payment_method_response(&payment_method)?; + + Ok(resp) + } + Err(e) => { + let pm_update = storage::PaymentMethodUpdate::StatusUpdate { + status: Some(enums::PaymentMethodStatus::Inactive), + }; + + db.update_payment_method( + &(state.into()), + &key_store, + payment_method, + pm_update, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to update payment method in db")?; + + Err(e) + } + }?; + + Ok(services::ApplicationResponse::Json(response)) +} + +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] +#[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] +pub async fn create_payment_method_in_db( + state: &SessionState, + req: &api::PaymentMethodCreate, + customer_id: &id_type::CustomerId, + payment_method_id: id_type::GlobalPaymentMethodId, + locker_id: Option, + merchant_id: &id_type::MerchantId, + pm_metadata: Option, + customer_acceptance: Option, + payment_method_data: crypto::OptionalEncryptableValue, + key_store: &domain::MerchantKeyStore, + connector_mandate_details: Option, + status: Option, + network_transaction_id: Option, + storage_scheme: enums::MerchantStorageScheme, + payment_method_billing_address: crypto::OptionalEncryptableValue, + card_scheme: Option, +) -> errors::CustomResult { + let db = &*state.store; + let client_secret = pm_types::PaymentMethodClientSecret::generate(&payment_method_id); + let current_time = common_utils::date_time::now(); + + let response = db + .insert_payment_method( + &state.into(), + key_store, + domain::PaymentMethod { + customer_id: customer_id.to_owned(), + merchant_id: merchant_id.to_owned(), + id: payment_method_id, + locker_id, + payment_method: Some(req.payment_method), + payment_method_type: Some(req.payment_method_type), + metadata: pm_metadata, + payment_method_data, + connector_mandate_details, + customer_acceptance, + client_secret: Some(client_secret), + status: status.unwrap_or(enums::PaymentMethodStatus::Active), + network_transaction_id: network_transaction_id.to_owned(), + created_at: current_time, + last_modified: current_time, + last_used_at: current_time, + payment_method_billing_address, + updated_by: None, + version: domain::consts::API_VERSION, + locker_fingerprint_id: None, + network_token_locker_id: None, + network_token_payment_method_data: None, + network_token_requestor_reference_id: None, + }, + storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to add payment method in db")?; + + Ok(response) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] +pub async fn create_payment_method_for_intent( + state: &SessionState, + metadata: Option, + customer_id: &id_type::CustomerId, + payment_method_id: id_type::GlobalPaymentMethodId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: enums::MerchantStorageScheme, + payment_method_billing_address: crypto::OptionalEncryptableValue, +) -> errors::CustomResult { + let db = &*state.store; + let client_secret = pm_types::PaymentMethodClientSecret::generate(&payment_method_id); + let current_time = common_utils::date_time::now(); + + let response = db + .insert_payment_method( + &state.into(), + key_store, + domain::PaymentMethod { + customer_id: customer_id.to_owned(), + merchant_id: merchant_id.to_owned(), + id: payment_method_id, + locker_id: None, + payment_method: None, + payment_method_type: None, + metadata, + payment_method_data: None, + connector_mandate_details: None, + customer_acceptance: None, + client_secret: Some(client_secret), + status: enums::PaymentMethodStatus::AwaitingData, + network_transaction_id: None, + created_at: current_time, + last_modified: current_time, + last_used_at: current_time, + payment_method_billing_address, + updated_by: None, + version: domain::consts::API_VERSION, + locker_fingerprint_id: None, + network_token_locker_id: None, + network_token_payment_method_data: None, + network_token_requestor_reference_id: None, + }, + storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to add payment method in db")?; + + Ok(response) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn create_pm_additional_data_update( + pmd: &api::PaymentMethodCreateData, + state: &SessionState, + key_store: &domain::MerchantKeyStore, + vault_id: Option, + payment_method: Option, + payment_method_type: Option, +) -> errors::RouterResult { + let card = match pmd.clone() { + api::PaymentMethodCreateData::Card(card) => api::PaymentMethodsData::Card(card.into()), + }; + + let pmd: Encryptable> = + cards::create_encrypted_data(state, key_store, card) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt Payment method data")?; + + let pm_update = storage::PaymentMethodUpdate::AdditionalDataUpdate { + status: Some(enums::PaymentMethodStatus::Active), + locker_id: vault_id, + payment_method, + payment_method_type, + payment_method_data: Some(pmd).map(Into::into), + network_token_requestor_reference_id: None, + network_token_locker_id: None, + network_token_payment_method_data: None, + }; + + Ok(pm_update) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +pub async fn vault_payment_method( + state: &SessionState, + pmd: &api::PaymentMethodCreateData, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, +) -> errors::RouterResult { + let db = &*state.store; + + // get fingerprint_id from locker + let fingerprint_id_from_locker = cards::get_fingerprint_id_from_locker(state, pmd) + .await + .attach_printable("Failed to get fingerprint_id from vault")?; + + // throw back error if payment method is duplicated + when( + Some( + db.find_payment_method_by_fingerprint_id( + &(state.into()), + key_store, + &fingerprint_id_from_locker, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to find payment method by fingerprint_id")?, + ) + .is_some(), + || { + Err(report!(errors::ApiErrorResponse::DuplicatePaymentMethod) + .attach_printable("Cannot vault duplicate payment method")) + }, + )?; + + let resp_from_locker = + cards::vault_payment_method_in_locker(state, merchant_account, pmd).await?; + + Ok(resp_from_locker) +} + +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] +async fn get_pm_list_context( + state: &SessionState, + payment_method: &enums::PaymentMethod, + _key_store: &domain::MerchantKeyStore, + pm: &domain::PaymentMethod, + _parent_payment_method_token: Option, + is_payment_associated: bool, +) -> Result, error_stack::Report> { + let payment_method_retrieval_context = match payment_method { + enums::PaymentMethod::Card => { + let card_details = cards::get_card_details_with_locker_fallback(pm, state).await?; + + card_details.as_ref().map(|card| PaymentMethodListContext { + card_details: Some(card.clone()), + #[cfg(feature = "payouts")] + bank_transfer_details: None, + hyperswitch_token_data: is_payment_associated.then_some( + storage::PaymentTokenData::permanent_card( + Some(pm.get_id().clone()), + pm.locker_id.clone().or(Some(pm.get_id().get_string_repr())), + pm.locker_id + .clone() + .unwrap_or(pm.get_id().get_string_repr()), + ), + ), + }) + } + + enums::PaymentMethod::BankDebit => { + // Retrieve the pm_auth connector details so that it can be tokenized + let bank_account_token_data = cards::get_bank_account_connector_details(pm) + .await + .unwrap_or_else(|err| { + logger::error!(error=?err); + None + }); + + bank_account_token_data.map(|data| { + let token_data = storage::PaymentTokenData::AuthBankDebit(data); + + PaymentMethodListContext { + card_details: None, + #[cfg(feature = "payouts")] + bank_transfer_details: None, + hyperswitch_token_data: is_payment_associated.then_some(token_data), + } + }) + } + + _ => Some(PaymentMethodListContext { + card_details: None, + #[cfg(feature = "payouts")] + bank_transfer_details: None, + hyperswitch_token_data: is_payment_associated.then_some( + storage::PaymentTokenData::temporary_generic(generate_id( + consts::ID_LENGTH, + "token", + )), + ), + }), + }; + + Ok(payment_method_retrieval_context) +} + +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] +pub async fn list_customer_payment_method_util( + state: SessionState, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + req: Option, + customer_id: Option, + is_payment_associated: bool, +) -> errors::RouterResponse { + let limit = req.as_ref().and_then(|pml_req| pml_req.limit); + + let (customer_id, payment_intent) = if is_payment_associated { + let cloned_secret = req.and_then(|r| r.client_secret.clone()); + let payment_intent = payment_helpers::verify_payment_intent_time_and_client_secret( + &state, + &merchant_account, + &key_store, + cloned_secret, + ) + .await?; + + ( + payment_intent + .as_ref() + .and_then(|pi| pi.customer_id.clone()), + payment_intent, + ) + } else { + (customer_id, None) + }; + + let resp = if let Some(cust) = customer_id { + Box::pin(list_customer_payment_method( + &state, + merchant_account, + key_store, + payment_intent, + &cust, + limit, + is_payment_associated, + )) + .await? + } else { + let response = api::CustomerPaymentMethodsListResponse { + customer_payment_methods: Vec::new(), + is_guest_customer: Some(true), + }; + services::ApplicationResponse::Json(response) + }; + + Ok(resp) +} + +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] +pub async fn list_customer_payment_method( + state: &SessionState, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + payment_intent: Option, + customer_id: &id_type::CustomerId, + limit: Option, + is_payment_associated: bool, +) -> errors::RouterResponse { + let db = &*state.store; + let key_manager_state = &(state).into(); + // let key = key_store.key.get_inner().peek(); + + let customer = db + .find_customer_by_merchant_reference_id_merchant_id( + key_manager_state, + customer_id, + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; + + let payments_info = payment_intent + .async_map(|pi| { + pm_types::SavedPMLPaymentsInfo::form_payments_info( + pi, + &merchant_account, + db, + key_manager_state, + &key_store, + ) + }) + .await + .transpose()?; + + let saved_payment_methods = db + .find_payment_method_by_customer_id_merchant_id_status( + key_manager_state, + &key_store, + customer_id, + merchant_account.get_id(), + common_enums::PaymentMethodStatus::Active, + limit, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; + + let mut filtered_saved_payment_methods_ctx = Vec::new(); + for pm in saved_payment_methods.into_iter() { + let payment_method = pm.payment_method.get_required_value("payment_method")?; + let parent_payment_method_token = + is_payment_associated.then(|| generate_id(consts::ID_LENGTH, "token")); + + let pm_list_context = get_pm_list_context( + state, + &payment_method, + &key_store, + &pm, + parent_payment_method_token.clone(), + is_payment_associated, + ) + .await?; + + if let Some(ctx) = pm_list_context { + filtered_saved_payment_methods_ctx.push((ctx, parent_payment_method_token, pm)); + } + } + + let pm_list_futures = filtered_saved_payment_methods_ctx + .into_iter() + .map(|ctx| { + generate_saved_pm_response( + state, + &key_store, + &merchant_account, + ctx, + &customer, + payments_info.as_ref(), + ) + }) + .collect::>(); + + let final_result = futures::future::join_all(pm_list_futures).await; + + let mut customer_pms = Vec::new(); + for result in final_result.into_iter() { + let pma = result.attach_printable("saved pm list failed")?; + customer_pms.push(pma); + } + + let mut response = api::CustomerPaymentMethodsListResponse { + customer_payment_methods: customer_pms, + is_guest_customer: is_payment_associated.then_some(false), //to return this key only when the request is tied to a payment intent + }; + + if is_payment_associated { + Box::pin(cards::perform_surcharge_ops( + payments_info.as_ref().map(|pi| pi.payment_intent.clone()), + state, + merchant_account, + key_store, + payments_info.and_then(|pi| pi.business_profile), + &mut response, + )) + .await?; + } + + Ok(services::ApplicationResponse::Json(response)) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +async fn generate_saved_pm_response( + state: &SessionState, + key_store: &domain::MerchantKeyStore, + merchant_account: &domain::MerchantAccount, + pm_list_context: ( + PaymentMethodListContext, + Option, + domain::PaymentMethod, + ), + customer: &domain::Customer, + payment_info: Option<&pm_types::SavedPMLPaymentsInfo>, +) -> Result> { + let (pm_list_context, parent_payment_method_token, pm) = pm_list_context; + let payment_method = pm.payment_method.get_required_value("payment_method")?; + + let bank_details = if payment_method == enums::PaymentMethod::BankDebit { + cards::get_masked_bank_details(&pm) + .await + .unwrap_or_else(|err| { + logger::error!(error=?err); + None + }) + } else { + None + }; + + let payment_method_billing = pm + .payment_method_billing_address + .clone() + .map(|decrypted_data| decrypted_data.into_inner().expose()) + .map(|decrypted_value| decrypted_value.parse_value("payment_method_billing_address")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to parse payment method billing address details")?; + + let connector_mandate_details = pm + .connector_mandate_details + .clone() + .map(|val| val.parse_value::("PaymentsMandateReference")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize to Payment Mandate Reference ")?; + + let (is_connector_agnostic_mit_enabled, requires_cvv, off_session_payment_flag, profile_id) = + payment_info + .map(|pi| { + ( + pi.is_connector_agnostic_mit_enabled, + pi.requires_cvv, + pi.off_session_payment_flag, + pi.business_profile + .as_ref() + .map(|profile| profile.get_id().to_owned()), + ) + }) + .unwrap_or((false, false, false, Default::default())); + + let mca_enabled = cards::get_mca_status( + state, + key_store, + profile_id, + merchant_account.get_id(), + is_connector_agnostic_mit_enabled, + connector_mandate_details, + pm.network_transaction_id.as_ref(), + ) + .await?; + + let requires_cvv = if is_connector_agnostic_mit_enabled { + requires_cvv + && !(off_session_payment_flag + && (pm.connector_mandate_details.is_some() || pm.network_transaction_id.is_some())) + } else { + requires_cvv && !(off_session_payment_flag && pm.connector_mandate_details.is_some()) + }; + + let pmd = if let Some(card) = pm_list_context.card_details.as_ref() { + Some(api::PaymentMethodListData::Card(card.clone())) + } else if cfg!(feature = "payouts") { + pm_list_context + .bank_transfer_details + .clone() + .map(api::PaymentMethodListData::Bank) + } else { + None + }; + + let pma = api::CustomerPaymentMethod { + payment_token: parent_payment_method_token.clone(), + payment_method_id: pm.get_id().get_string_repr(), + customer_id: pm.customer_id.to_owned(), + payment_method, + payment_method_type: pm.payment_method_type, + payment_method_data: pmd, + metadata: pm.metadata.clone(), + recurring_enabled: mca_enabled, + created: Some(pm.created_at), + bank: bank_details, + surcharge_details: None, + requires_cvv: requires_cvv + && !(off_session_payment_flag && pm.connector_mandate_details.is_some()), + last_used_at: Some(pm.last_used_at), + is_default: customer.default_payment_method_id.is_some() + && customer.default_payment_method_id.as_ref() == Some(&pm.get_id().get_string_repr()), + billing: payment_method_billing, + }; + + payment_info + .async_map(|pi| { + pi.perform_payment_ops(state, parent_payment_method_token, &pma, pm_list_context) + }) + .await + .transpose()?; + + Ok(pma) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl pm_types::SavedPMLPaymentsInfo { + pub async fn form_payments_info( + payment_intent: storage::PaymentIntent, + merchant_account: &domain::MerchantAccount, + db: &dyn StorageInterface, + key_manager_state: &util_types::keymanager::KeyManagerState, + key_store: &domain::MerchantKeyStore, + ) -> errors::RouterResult { + let requires_cvv = db + .find_config_by_key_unwrap_or( + format!( + "{}_requires_cvv", + merchant_account.get_id().get_string_repr() + ) + .as_str(), + Some("true".to_string()), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch requires_cvv config")? + .config + != "false"; + + let off_session_payment_flag = matches!( + payment_intent.setup_future_usage, + Some(common_enums::FutureUsage::OffSession) + ); + + let profile_id = payment_intent + .profile_id + .as_ref() + .get_required_value("profile_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("profile_id is not set in payment_intent")? + .clone(); + + let business_profile = core_utils::validate_and_get_business_profile( + db, + key_manager_state, + key_store, + Some(profile_id).as_ref(), + merchant_account.get_id(), + ) + .await?; + + let is_connector_agnostic_mit_enabled = business_profile + .as_ref() + .and_then(|business_profile| business_profile.is_connector_agnostic_mit_enabled) + .unwrap_or(false); + + Ok(Self { + payment_intent, + business_profile, + requires_cvv, + off_session_payment_flag, + is_connector_agnostic_mit_enabled, + }) + } + + pub async fn perform_payment_ops( + &self, + state: &SessionState, + parent_payment_method_token: Option, + pma: &api::CustomerPaymentMethod, + pm_list_context: PaymentMethodListContext, + ) -> errors::RouterResult<()> { + let token = parent_payment_method_token + .as_ref() + .get_required_value("parent_payment_method_token")?; + let hyperswitch_token_data = pm_list_context + .hyperswitch_token_data + .get_required_value("PaymentTokenData")?; + + let intent_fulfillment_time = self + .business_profile + .as_ref() + .and_then(|b_profile| b_profile.get_order_fulfillment_time()) + .unwrap_or(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME); + + pm_routes::ParentPaymentMethodToken::create_key_for_token((token, pma.payment_method)) + .insert(intent_fulfillment_time, hyperswitch_token_data, state) + .await?; + + if let Some(metadata) = pma.metadata.clone() { + let pm_metadata_vec: pm_transforms::PaymentMethodMetadata = metadata + .parse_value("PaymentMethodMetadata") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Failed to deserialize metadata to PaymentmethodMetadata struct", + )?; + + let redis_conn = state + .store + .get_redis_conn() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get redis connection")?; + + for pm_metadata in pm_metadata_vec.payment_method_tokenization { + let key = format!( + "pm_token_{}_{}_{}", + token, pma.payment_method, pm_metadata.0 + ); + + redis_conn + .set_key_with_expiry(&key, pm_metadata.1, intent_fulfillment_time) + .await + .change_context(errors::StorageError::KVError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to add data in redis")?; + } + } + + Ok(()) + } +} diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 22350f77e69..79d1226f13a 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -69,8 +69,6 @@ use super::surcharge_decision_configs::{ use crate::routes::app::SessionStateInfo; #[cfg(feature = "payouts")] use crate::types::domain::types::AsyncLift; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -use crate::utils::{self}; use crate::{ configs::{ defaults::{get_billing_required_fields, get_shipping_required_fields}, @@ -95,8 +93,14 @@ use crate::{ storage::{self, enums, PaymentMethodListContext, PaymentTokenData}, transformers::ForeignTryFrom, }, + utils, utils::OptionExt, }; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use crate::{ + consts as router_consts, core::payment_methods as pm_core, headers, + types::payment_methods as pm_types, utils::ConnectorResponseExt, +}; #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") @@ -217,32 +221,35 @@ pub async fn create_payment_method( Ok(response) } -#[cfg(all( - feature = "v2", - feature = "payment_methods_v2", - feature = "customer_v2" -))] -#[instrument(skip_all)] -#[allow(clippy::too_many_arguments)] -pub async fn create_payment_method( - _state: &routes::SessionState, - _req: &api::PaymentMethodCreate, - _customer_id: &id_type::CustomerId, - _payment_method_id: &str, - _locker_id: Option, - _merchant_id: &id_type::MerchantId, - _pm_metadata: Option, - _customer_acceptance: Option, - _payment_method_data: Option, - _key_store: &domain::MerchantKeyStore, - _connector_mandate_details: Option, - _status: Option, - _network_transaction_id: Option, - _storage_scheme: MerchantStorageScheme, - _payment_method_billing_address: Option, - _card_scheme: Option, -) -> errors::CustomResult { - todo!() +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +async fn create_vault_request( + jwekey: &settings::Jwekey, + locker: &settings::Locker, + payload: Vec, +) -> errors::CustomResult { + let private_key = jwekey.vault_private_key.peek().as_bytes(); + + let jws = services::encryption::jws_sign_payload( + &payload, + &locker.locker_signing_key_id, + private_key, + ) + .await + .change_context(errors::VaultError::RequestEncryptionFailed)?; + + let jwe_payload = payment_methods::create_jwe_body_for_vault(jwekey, &jws).await?; + + let mut url = locker.host.to_owned(); + url.push_str(R::get_vaulting_request_url()); + let mut request = services::Request::new(services::Method::Post, &url); + request.add_header( + headers::CONTENT_TYPE, + router_consts::VAULT_HEADER_CONTENT_TYPE.into(), + ); + request.set_body(common_utils::request::RequestContent::Json(Box::new( + jwe_payload, + ))); + Ok(request) } #[cfg(all( @@ -911,17 +918,6 @@ pub async fn get_client_secret_or_add_payment_method( } } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[instrument(skip_all)] -pub async fn get_client_secret_or_add_payment_method( - _state: &routes::SessionState, - _req: api::PaymentMethodCreate, - _merchant_account: &domain::MerchantAccount, - _key_store: &domain::MerchantKeyStore, -) -> errors::RouterResponse { - todo!() -} - #[instrument(skip_all)] pub fn authenticate_pm_client_secret_and_check_expiry( req_client_secret: &String, @@ -1417,13 +1413,64 @@ pub async fn add_payment_method( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[instrument(skip_all)] -pub async fn add_payment_method( - _state: &routes::SessionState, - _req: api::PaymentMethodCreate, - _merchant_account: &domain::MerchantAccount, - _key_store: &domain::MerchantKeyStore, -) -> errors::RouterResponse { - todo!() +pub async fn get_fingerprint_id_from_locker< + D: pm_types::VaultingDataInterface + serde::Serialize, +>( + state: &routes::SessionState, + data: &D, +) -> errors::RouterResult { + let key = data.get_vaulting_data_key(); + let data = serde_json::to_value(data) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode Vaulting data to value")? + .to_string(); + + let payload = pm_types::VaultFingerprintRequest { key, data } + .encode_to_vec() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode VaultFingerprintRequest")?; + + let resp = call_to_vault::(state, payload) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get response from locker")?; + + let fingerprint_resp: pm_types::VaultFingerprintResponse = resp + .parse_struct("VaultFingerprintResp") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse data into VaultFingerprintResp")?; + + Ok(fingerprint_resp.fingerprint_id) +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +pub async fn vault_payment_method_in_locker( + state: &routes::SessionState, + merchant_account: &domain::MerchantAccount, + pmd: &api::PaymentMethodCreateData, +) -> errors::RouterResult { + let payload = pm_types::AddVaultRequest { + entity_id: merchant_account.get_id().to_owned(), + vault_id: uuid::Uuid::now_v7().to_string(), + data: pmd.clone(), + ttl: state.conf.locker.ttl_for_storage_in_secs, + } + .encode_to_vec() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode AddVaultRequest")?; + + let resp = call_to_vault::(state, payload) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to get response from locker")?; + + let stored_pm_resp: pm_types::AddVaultResponse = resp + .parse_struct("AddVaultResponse") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse data into AddVaultResponse")?; + + Ok(stored_pm_resp) } #[cfg(all( @@ -1506,40 +1553,7 @@ pub async fn insert_payment_method( storage_scheme: MerchantStorageScheme, payment_method_billing_address: Option, ) -> errors::RouterResult { - let pm_card_details = match &resp.payment_method_data { - Some(api::PaymentMethodResponseData::Card(card_data)) => Some(PaymentMethodsData::Card( - CardDetailsPaymentMethod::from(card_data.clone()), - )), - _ => None, - }; - - let pm_data_encrypted: Option>> = pm_card_details - .clone() - .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) - .await - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt payment method data")?; - - create_payment_method( - state, - req, - customer_id, - &resp.payment_method_id, - locker_id, - merchant_id, - pm_metadata, - customer_acceptance, - pm_data_encrypted.map(Into::into), - key_store, - connector_mandate_details, - None, - network_transaction_id, - storage_scheme, - payment_method_billing_address, - None, - ) - .await + todo!() } #[cfg(all( @@ -2225,6 +2239,37 @@ pub async fn add_card_to_hs_locker( Ok(stored_card) } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all)] +pub async fn call_to_vault( + state: &routes::SessionState, + payload: Vec, +) -> errors::CustomResult { + let locker = &state.conf.locker; + let jwekey = state.conf.jwekey.get_inner(); + + let request = create_vault_request::(jwekey, locker, payload).await?; + let response = services::call_connector_api(state, request, "vault_in_locker") + .await + .change_context(errors::VaultError::VaultAPIError); + + let jwe_body: services::JweBody = response + .get_response_inner("JweBody") + .change_context(errors::VaultError::ResponseDeserializationFailed) + .attach_printable("Failed to get JweBody from vault response")?; + + let decrypted_payload = payment_methods::get_decrypted_vault_response_payload( + jwekey, + jwe_body, + locker.decryption_scheme.clone(), + ) + .await + .change_context(errors::VaultError::ResponseDecryptionFailed) + .attach_printable("Error getting decrypted vault response payload")?; + + Ok(decrypted_payload) +} + #[instrument(skip_all)] pub async fn call_locker_api( state: &routes::SessionState, @@ -3819,6 +3864,10 @@ fn should_collect_shipping_or_billing_details_from_wallet_connector( } } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2"), +))] async fn validate_payment_method_and_client_secret( state: &routes::SessionState, cs: &String, @@ -4235,63 +4284,6 @@ fn filter_recurring_based( recurring_enabled.map_or(true, |enabled| payment_method.recurring_enabled == enabled) } -#[cfg(all( - feature = "v2", - feature = "payment_methods_v2", - feature = "customer_v2" -))] -pub async fn list_customer_payment_method_util( - state: routes::SessionState, - merchant_account: domain::MerchantAccount, - key_store: domain::MerchantKeyStore, - req: Option, - customer_id: Option, - is_payment_associated: bool, -) -> errors::RouterResponse { - let limit = req.as_ref().and_then(|pml_req| pml_req.limit); - - let (customer_id, payment_intent) = if is_payment_associated { - let cloned_secret = req.and_then(|r| r.client_secret.clone()); - let payment_intent = helpers::verify_payment_intent_time_and_client_secret( - &state, - &merchant_account, - &key_store, - cloned_secret, - ) - .await?; - - ( - payment_intent - .as_ref() - .and_then(|pi| pi.customer_id.clone()), - payment_intent, - ) - } else { - (customer_id, None) - }; - - let resp = if let Some(cust) = customer_id { - Box::pin(list_customer_payment_method( - &state, - merchant_account, - key_store, - payment_intent, - &cust, - limit, - is_payment_associated, - )) - .await? - } else { - let response = api::CustomerPaymentMethodsListResponse { - customer_payment_methods: Vec::new(), - is_guest_customer: Some(true), - }; - services::ApplicationResponse::Json(response) - }; - - Ok(resp) -} - #[cfg(all( any(feature = "v2", feature = "v1"), not(feature = "payment_methods_v2"), @@ -4615,6 +4607,11 @@ pub async fn list_customer_payment_method( Ok(services::ApplicationResponse::Json(response)) } +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") +))] async fn get_pm_list_context( state: &routes::SessionState, payment_method: &enums::PaymentMethod, @@ -4706,7 +4703,11 @@ async fn get_pm_list_context( Ok(payment_method_retrieval_context) } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_v2"), + not(feature = "payment_methods_v2") +))] async fn perform_surcharge_ops( payment_intent: Option, state: &routes::SessionState, @@ -4751,8 +4752,8 @@ async fn perform_surcharge_ops( Ok(()) } -#[cfg(all(feature = "v2", feature = "payment_v2"))] -async fn perform_surcharge_ops( +#[cfg(all(feature = "v2", feature = "payment_v2", feature = "payment_methods_v2"))] +pub async fn perform_surcharge_ops( _payment_intent: Option, _state: &routes::SessionState, _merchant_account: domain::MerchantAccount, @@ -4763,370 +4764,6 @@ async fn perform_surcharge_ops( todo!() } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -struct SavedPMLPaymentsInfo { - pub payment_intent: storage::PaymentIntent, - pub business_profile: Option, - pub requires_cvv: bool, - pub off_session_payment_flag: bool, - pub is_connector_agnostic_mit_enabled: bool, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -impl SavedPMLPaymentsInfo { - pub async fn form_payments_info( - payment_intent: storage::PaymentIntent, - merchant_account: &domain::MerchantAccount, - db: &dyn db::StorageInterface, - key_manager_state: &KeyManagerState, - key_store: &domain::MerchantKeyStore, - ) -> errors::RouterResult { - let requires_cvv = db - .find_config_by_key_unwrap_or( - format!( - "{}_requires_cvv", - merchant_account.get_id().get_string_repr() - ) - .as_str(), - Some("true".to_string()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch requires_cvv config")? - .config - != "false"; - - let off_session_payment_flag = matches!( - payment_intent.setup_future_usage, - Some(common_enums::FutureUsage::OffSession) - ); - - let profile_id = payment_intent - .profile_id - .as_ref() - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("profile_id is not set in payment_intent")? - .clone(); - - let business_profile = core_utils::validate_and_get_business_profile( - db, - key_manager_state, - key_store, - Some(profile_id).as_ref(), - merchant_account.get_id(), - ) - .await?; - - let is_connector_agnostic_mit_enabled = business_profile - .as_ref() - .and_then(|business_profile| business_profile.is_connector_agnostic_mit_enabled) - .unwrap_or(false); - - Ok(Self { - payment_intent, - business_profile, - requires_cvv, - off_session_payment_flag, - is_connector_agnostic_mit_enabled, - }) - } - - pub async fn perform_payment_ops( - &self, - state: &routes::SessionState, - parent_payment_method_token: Option, - pma: &api::CustomerPaymentMethod, - pm_list_context: PaymentMethodListContext, - ) -> errors::RouterResult<()> { - let token = parent_payment_method_token - .as_ref() - .get_required_value("parent_payment_method_token")?; - let hyperswitch_token_data = pm_list_context - .hyperswitch_token_data - .get_required_value("PaymentTokenData")?; - - let intent_fulfillment_time = self - .business_profile - .as_ref() - .and_then(|b_profile| b_profile.get_order_fulfillment_time()) - .unwrap_or(consts::DEFAULT_INTENT_FULFILLMENT_TIME); - - ParentPaymentMethodToken::create_key_for_token((token, pma.payment_method)) - .insert(intent_fulfillment_time, hyperswitch_token_data, state) - .await?; - - if let Some(metadata) = pma.metadata.clone() { - let pm_metadata_vec: payment_methods::PaymentMethodMetadata = metadata - .parse_value("PaymentMethodMetadata") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Failed to deserialize metadata to PaymentmethodMetadata struct", - )?; - - let redis_conn = state - .store - .get_redis_conn() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get redis connection")?; - - for pm_metadata in pm_metadata_vec.payment_method_tokenization { - let key = format!( - "pm_token_{}_{}_{}", - token, pma.payment_method, pm_metadata.0 - ); - - redis_conn - .set_key_with_expiry(&key, pm_metadata.1, intent_fulfillment_time) - .await - .change_context(errors::StorageError::KVError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to add data in redis")?; - } - } - - Ok(()) - } -} - -#[cfg(all( - feature = "v2", - feature = "payment_methods_v2", - feature = "customer_v2" -))] -pub async fn list_customer_payment_method( - state: &routes::SessionState, - merchant_account: domain::MerchantAccount, - key_store: domain::MerchantKeyStore, - payment_intent: Option, - customer_id: &id_type::CustomerId, - limit: Option, - is_payment_associated: bool, -) -> errors::RouterResponse { - let db = &*state.store; - let key_manager_state = &(state).into(); - // let key = key_store.key.get_inner().peek(); - - let customer = db - .find_customer_by_merchant_reference_id_merchant_id( - key_manager_state, - customer_id, - merchant_account.get_id(), - &key_store, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; - - let payments_info = payment_intent - .async_map(|pi| { - SavedPMLPaymentsInfo::form_payments_info( - pi, - &merchant_account, - db, - key_manager_state, - &key_store, - ) - }) - .await - .transpose()?; - - let saved_payment_methods = db - .find_payment_method_by_customer_id_merchant_id_status( - key_manager_state, - &key_store, - customer_id, - merchant_account.get_id(), - common_enums::PaymentMethodStatus::Active, - limit, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; - - let mut filtered_saved_payment_methods_ctx = Vec::new(); - for pm in saved_payment_methods.into_iter() { - logger::debug!( - "Fetching payment method from locker for payment_method_id: {}", - pm.id - ); - let payment_method = pm.payment_method.get_required_value("payment_method")?; - let parent_payment_method_token = - is_payment_associated.then(|| generate_id(consts::ID_LENGTH, "token")); - - let pm_list_context = get_pm_list_context( - state, - &payment_method, - &key_store, - &pm, - parent_payment_method_token.clone(), - is_payment_associated, - ) - .await?; - - if let Some(ctx) = pm_list_context { - filtered_saved_payment_methods_ctx.push((ctx, parent_payment_method_token, pm)); - } - } - - let pm_list_futures = filtered_saved_payment_methods_ctx - .into_iter() - .map(|ctx| { - generate_saved_pm_response( - state, - &key_store, - &merchant_account, - ctx, - &customer, - payments_info.as_ref(), - ) - }) - .collect::>(); - - let final_result = futures::future::join_all(pm_list_futures).await; - - let mut customer_pms = Vec::new(); - for result in final_result.into_iter() { - let pma = result.attach_printable("saved pm list failed")?; - customer_pms.push(pma); - } - - let mut response = api::CustomerPaymentMethodsListResponse { - customer_payment_methods: customer_pms, - is_guest_customer: is_payment_associated.then_some(false), //to return this key only when the request is tied to a payment intent - }; - - if is_payment_associated { - Box::pin(perform_surcharge_ops( - payments_info.as_ref().map(|pi| pi.payment_intent.clone()), - state, - merchant_account, - key_store, - payments_info.and_then(|pi| pi.business_profile), - &mut response, - )) - .await?; - } - - Ok(services::ApplicationResponse::Json(response)) -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -async fn generate_saved_pm_response( - state: &routes::SessionState, - key_store: &domain::MerchantKeyStore, - merchant_account: &domain::MerchantAccount, - pm_list_context: ( - PaymentMethodListContext, - Option, - domain::PaymentMethod, - ), - customer: &domain::Customer, - payment_info: Option<&SavedPMLPaymentsInfo>, -) -> Result> { - let (pm_list_context, parent_payment_method_token, pm) = pm_list_context; - let payment_method = pm.payment_method.get_required_value("payment_method")?; - - let bank_details = if payment_method == enums::PaymentMethod::BankDebit { - get_masked_bank_details(&pm).await.unwrap_or_else(|err| { - logger::error!(error=?err); - None - }) - } else { - None - }; - - let payment_method_billing = pm - .payment_method_billing_address - .clone() - .map(|decrypted_data| decrypted_data.into_inner().expose()) - .map(|decrypted_value| decrypted_value.parse_value("payment_method_billing_address")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to parse payment method billing address details")?; - - let connector_mandate_details = pm - .connector_mandate_details - .clone() - .map(|val| val.parse_value::("PaymentsMandateReference")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize to Payment Mandate Reference ")?; - - let (is_connector_agnostic_mit_enabled, requires_cvv, off_session_payment_flag, profile_id) = - payment_info - .map(|pi| { - ( - pi.is_connector_agnostic_mit_enabled, - pi.requires_cvv, - pi.off_session_payment_flag, - pi.business_profile - .as_ref() - .map(|profile| profile.get_id().to_owned()), - ) - }) - .unwrap_or((false, false, false, Default::default())); - - let mca_enabled = get_mca_status( - state, - key_store, - profile_id, - merchant_account.get_id(), - is_connector_agnostic_mit_enabled, - connector_mandate_details, - pm.network_transaction_id.as_ref(), - ) - .await?; - - let requires_cvv = if is_connector_agnostic_mit_enabled { - requires_cvv - && !(off_session_payment_flag - && (pm.connector_mandate_details.is_some() || pm.network_transaction_id.is_some())) - } else { - requires_cvv && !(off_session_payment_flag && pm.connector_mandate_details.is_some()) - }; - - let pmd = if let Some(card) = pm_list_context.card_details.as_ref() { - Some(api::PaymentMethodListData::Card(card.clone())) - } else if cfg!(feature = "payouts") { - pm_list_context - .bank_transfer_details - .clone() - .map(api::PaymentMethodListData::Bank) - } else { - None - }; - - let pma = api::CustomerPaymentMethod { - payment_token: parent_payment_method_token.clone(), - payment_method_id: pm.get_id().clone(), - customer_id: pm.customer_id.to_owned(), - payment_method, - payment_method_type: pm.payment_method_type, - payment_method_data: pmd, - metadata: pm.metadata.clone(), - recurring_enabled: mca_enabled, - created: Some(pm.created_at), - bank: bank_details, - surcharge_details: None, - requires_cvv: requires_cvv - && !(off_session_payment_flag && pm.connector_mandate_details.is_some()), - last_used_at: Some(pm.last_used_at), - is_default: customer.default_payment_method_id.is_some() - && customer.default_payment_method_id.as_ref() == Some(pm.get_id()), - billing: payment_method_billing, - }; - - payment_info - .async_map(|pi| { - pi.perform_payment_ops(state, parent_payment_method_token, &pma, pm_list_context) - }) - .await - .transpose()?; - - Ok(pma) -} - pub async fn get_mca_status( state: &routes::SessionState, key_store: &domain::MerchantKeyStore, @@ -5236,24 +4873,7 @@ pub async fn get_card_details_with_locker_fallback( pm: &domain::PaymentMethod, state: &routes::SessionState, ) -> errors::RouterResult> { - let card_decrypted = pm - .payment_method_data - .clone() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); - - Ok(if let Some(crd) = card_decrypted { - Some(crd) - } else { - logger::debug!( - "Getting card details from locker as it is not found in payment methods table" - ); - Some(get_card_details_from_locker(state, pm).await?) - }) + todo!() } #[cfg(all( @@ -5290,25 +4910,13 @@ pub async fn get_card_details_without_locker_fallback( pm: &domain::PaymentMethod, state: &routes::SessionState, ) -> errors::RouterResult { - let card_decrypted = pm - .payment_method_data - .clone() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); - Ok(if let Some(crd) = card_decrypted { - crd - } else { - logger::debug!( - "Getting card details from locker as it is not found in payment methods table" - ); - get_card_details_from_locker(state, pm).await? - }) + todo!() } +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] pub async fn get_card_details_from_locker( state: &routes::SessionState, pm: &domain::PaymentMethod, @@ -5328,6 +4936,10 @@ pub async fn get_card_details_from_locker( .attach_printable("Get Card Details Failed") } +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] pub async fn get_lookup_key_from_locker( state: &routes::SessionState, payment_token: &str, @@ -5348,7 +4960,7 @@ pub async fn get_lookup_key_from_locker( Ok(resp) } -async fn get_masked_bank_details( +pub async fn get_masked_bank_details( pm: &domain::PaymentMethod, ) -> errors::RouterResult> { let payment_method_data = pm @@ -5377,7 +4989,7 @@ async fn get_masked_bank_details( } } -async fn get_bank_account_connector_details( +pub async fn get_bank_account_connector_details( pm: &domain::PaymentMethod, ) -> errors::RouterResult> { let payment_method_data = pm @@ -5604,8 +5216,16 @@ pub async fn get_bank_from_hs_locker( } } +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] pub struct TempLockerCardSupport; +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] impl TempLockerCardSupport { #[instrument(skip_all)] async fn create_payment_method_data_in_temp_locker( @@ -5761,53 +5381,7 @@ pub async fn retrieve_payment_method( key_store: domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, ) -> errors::RouterResponse { - let db = state.store.as_ref(); - let pm = db - .find_payment_method( - &((&state).into()), - &key_store, - &pm.payment_method_id, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; - - let card = if pm.payment_method == Some(enums::PaymentMethod::Card) { - let card_detail = if state.conf.locker.locker_enabled { - let card = get_card_from_locker( - &state, - &pm.customer_id, - &pm.merchant_id, - pm.locker_id.as_ref().unwrap_or(pm.get_id()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting card from card vault")?; - payment_methods::get_card_detail(&pm, card) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while getting card details from locker")? - } else { - get_card_details_without_locker_fallback(&pm, &state).await? - }; - Some(card_detail) - } else { - None - }; - Ok(services::ApplicationResponse::Json( - api::PaymentMethodResponse { - merchant_id: pm.merchant_id.to_owned(), - customer_id: pm.customer_id.to_owned(), - payment_method_id: pm.get_id().clone(), - payment_method: pm.payment_method, - payment_method_type: pm.payment_method_type, - metadata: pm.metadata, - created: Some(pm.created_at), - recurring_enabled: false, - payment_method_data: card.clone().map(api::PaymentMethodResponseData::Card), - last_used_at: Some(pm.last_used_at), - client_secret: pm.client_secret, - }, - )) + todo!() } #[cfg(all( diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 17477141492..d0f63a2dc04 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -2,6 +2,8 @@ use std::str::FromStr; use api_models::{enums as api_enums, payment_methods::Card}; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use common_utils::ext_traits::ValueExt; use common_utils::{ ext_traits::{Encode, StringExt}, id_type, @@ -249,6 +251,92 @@ pub async fn get_decrypted_response_payload( .attach_printable("Jws Decryption failed for JwsBody for vault") } +pub async fn get_decrypted_vault_response_payload( + jwekey: &settings::Jwekey, + jwe_body: encryption::JweBody, + decryption_scheme: settings::DecryptionScheme, +) -> CustomResult { + let public_key = jwekey.vault_encryption_key.peek().as_bytes(); + + let private_key = jwekey.vault_private_key.peek().as_bytes(); + + let jwt = get_dotted_jwe(jwe_body); + let alg = match decryption_scheme { + settings::DecryptionScheme::RsaOaep => jwe::RSA_OAEP, + settings::DecryptionScheme::RsaOaep256 => jwe::RSA_OAEP_256, + }; + + let jwe_decrypted = encryption::decrypt_jwe( + &jwt, + encryption::KeyIdCheck::SkipKeyIdCheck, + private_key, + alg, + ) + .await + .change_context(errors::VaultError::SaveCardFailed) + .attach_printable("Jwe Decryption failed for JweBody for vault")?; + + let jws = jwe_decrypted + .parse_struct("JwsBody") + .change_context(errors::VaultError::ResponseDeserializationFailed)?; + let jws_body = get_dotted_jws(jws); + + encryption::verify_sign(jws_body, public_key) + .change_context(errors::VaultError::SaveCardFailed) + .attach_printable("Jws Decryption failed for JwsBody for vault") +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn create_jwe_body_for_vault( + jwekey: &settings::Jwekey, + jws: &str, +) -> CustomResult { + let jws_payload: Vec<&str> = jws.split('.').collect(); + + let generate_jws_body = |payload: Vec<&str>| -> Option { + Some(encryption::JwsBody { + header: payload.first()?.to_string(), + payload: payload.get(1)?.to_string(), + signature: payload.get(2)?.to_string(), + }) + }; + + let jws_body = + generate_jws_body(jws_payload).ok_or(errors::VaultError::RequestEncryptionFailed)?; + + let payload = jws_body + .encode_to_vec() + .change_context(errors::VaultError::RequestEncodingFailed)?; + + let public_key = jwekey.vault_encryption_key.peek().as_bytes(); + + let jwe_encrypted = encryption::encrypt_jwe( + &payload, + public_key, + encryption::EncryptionAlgorithm::A256GCM, + None, + ) + .await + .change_context(errors::VaultError::SaveCardFailed) + .attach_printable("Error on jwe encrypt")?; + let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect(); + + let generate_jwe_body = |payload: Vec<&str>| -> Option { + Some(encryption::JweBody { + header: payload.first()?.to_string(), + iv: payload.get(2)?.to_string(), + encrypted_payload: payload.get(3)?.to_string(), + tag: payload.get(4)?.to_string(), + encrypted_key: payload.get(1)?.to_string(), + }) + }; + + let jwe_body = + generate_jwe_body(jwe_payload).ok_or(errors::VaultError::RequestEncodingFailed)?; + + Ok(jwe_body) +} + pub async fn mk_basilisk_req( jwekey: &settings::Jwekey, jws: &str, @@ -428,14 +516,50 @@ pub fn mk_add_card_response_hs( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub fn mk_add_card_response_hs( - _card: api::CardDetail, - _card_reference: String, - _req: api::PaymentMethodCreate, - _merchant_id: &id_type::MerchantId, + card: api::CardDetail, + card_reference: String, + req: api::PaymentMethodCreate, + merchant_id: &id_type::MerchantId, ) -> api::PaymentMethodResponse { todo!() } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub fn generate_payment_method_response( + pm: &domain::PaymentMethod, +) -> errors::RouterResult { + let pmd = pm + .payment_method_data + .clone() + .map(|data| data.into_inner().expose()) + .map(|decrypted_value| decrypted_value.parse_value("PaymentMethodsData")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to parse PaymentMethodsData")? + .and_then(|data| match data { + api::PaymentMethodsData::Card(card) => { + Some(api::PaymentMethodResponseData::Card(card.into())) + } + _ => None, + }); + + let resp = api::PaymentMethodResponse { + merchant_id: pm.merchant_id.to_owned(), + customer_id: pm.customer_id.to_owned(), + payment_method_id: pm.id.get_string_repr(), + payment_method: pm.payment_method, + payment_method_type: pm.payment_method_type, + metadata: pm.metadata.clone(), + created: Some(pm.created_at), + recurring_enabled: false, + last_used_at: Some(pm.last_used_at), + client_secret: pm.client_secret.clone(), + payment_method_data: pmd, + }; + + Ok(resp) +} + #[allow(clippy::too_many_arguments)] pub async fn mk_get_card_request_hs( jwekey: &settings::Jwekey, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 0ec14bd69ed..0af5bf6037a 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3807,6 +3807,25 @@ where .await } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( + state: &SessionState, + payment_data: &mut D, + routing_data: &mut storage::RoutingData, + connectors: Vec, + mandate_type: Option, + is_connector_agnostic_mit_enabled: Option, +) -> RouterResult +where + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, +{ + todo!() +} + +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] #[allow(clippy::too_many_arguments)] pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( state: &SessionState, @@ -3970,6 +3989,26 @@ where } } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[allow(clippy::too_many_arguments)] +pub async fn decide_connector_for_normal_or_recurring_payment( + state: &SessionState, + payment_data: &mut D, + routing_data: &mut storage::RoutingData, + connectors: Vec, + is_connector_agnostic_mit_enabled: Option, + payment_method_info: &domain::PaymentMethod, +) -> RouterResult +where + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, +{ + todo!() +} + +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] #[allow(clippy::too_many_arguments)] pub async fn decide_connector_for_normal_or_recurring_payment( state: &SessionState, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index a1d92d8a992..ce92c1ab007 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1781,6 +1781,23 @@ pub async fn retrieve_payment_method_with_temporary_token( }) } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn retrieve_card_with_permanent_token( + state: &SessionState, + locker_id: &str, + _payment_method_id: &common_utils::id_type::GlobalPaymentMethodId, + payment_intent: &PaymentIntent, + card_token_data: Option<&domain::CardToken>, + _merchant_key_store: &domain::MerchantKeyStore, + _storage_scheme: enums::MerchantStorageScheme, +) -> RouterResult { + todo!() +} + +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] #[allow(clippy::too_many_arguments)] pub async fn retrieve_card_with_permanent_token( state: &SessionState, @@ -1986,6 +2003,20 @@ pub async fn fetch_card_details_from_locker( Ok(domain::PaymentMethodData::Card(api_card.into())) } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn retrieve_payment_method_from_db_with_token_data( + state: &SessionState, + merchant_key_store: &domain::MerchantKeyStore, + token_data: &storage::PaymentTokenData, + storage_scheme: storage::enums::MerchantStorageScheme, +) -> RouterResult> { + todo!() +} + +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] pub async fn retrieve_payment_method_from_db_with_token_data( state: &SessionState, merchant_key_store: &domain::MerchantKeyStore, @@ -2082,6 +2113,27 @@ pub async fn retrieve_payment_token_data( Ok(token_data) } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn make_pm_data<'a, F: Clone, R, D>( + _operation: BoxedOperation<'a, F, R, D>, + _state: &'a SessionState, + _payment_data: &mut PaymentData, + _merchant_key_store: &domain::MerchantKeyStore, + _customer: &Option, + _storage_scheme: common_enums::enums::MerchantStorageScheme, + _business_profile: Option<&domain::BusinessProfile>, +) -> RouterResult<( + BoxedOperation<'a, F, R, D>, + Option, + Option, +)> { + todo!() +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] pub async fn make_pm_data<'a, F: Clone, R, D>( operation: BoxedOperation<'a, F, R, D>, state: &'a SessionState, @@ -5100,6 +5152,21 @@ pub fn update_additional_payment_data_with_connector_response_pm_data( .attach_printable("Failed to encode additional pm data") } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn get_payment_method_details_from_payment_token( + state: &SessionState, + payment_attempt: &PaymentAttempt, + payment_intent: &PaymentIntent, + key_store: &domain::MerchantKeyStore, + storage_scheme: enums::MerchantStorageScheme, +) -> RouterResult> { + todo!() +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] pub async fn get_payment_method_details_from_payment_token( state: &SessionState, payment_attempt: &PaymentAttempt, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index f397ecd8a2a..a9cd1ae4d84 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -978,6 +978,35 @@ impl ValidateRequest> f } impl PaymentCreate { + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + #[instrument(skip_all)] + #[allow(clippy::too_many_arguments)] + pub async fn make_payment_attempt( + payment_id: &common_utils::id_type::PaymentId, + merchant_id: &common_utils::id_type::MerchantId, + organization_id: &common_utils::id_type::OrganizationId, + money: (api::Amount, enums::Currency), + payment_method: Option, + payment_method_type: Option, + request: &api::PaymentsRequest, + browser_info: Option, + state: &SessionState, + payment_method_billing_address_id: Option, + payment_method_info: &Option, + _key_store: &domain::MerchantKeyStore, + profile_id: common_utils::id_type::ProfileId, + customer_acceptance: &Option, + ) -> RouterResult<( + storage::PaymentAttemptNew, + Option, + )> { + todo!() + } + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn make_payment_attempt( diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index fb09b4d8b60..ae6ac330679 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -88,6 +88,26 @@ impl PostUpdateTracker, types::PaymentsAuthor Ok(payment_data) } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + async fn save_pm_and_mandate<'b>( + &self, + state: &SessionState, + resp: &types::RouterData, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + business_profile: &domain::BusinessProfile, + ) -> CustomResult<(), errors::ApiErrorResponse> + where + F: 'b + Clone + Send + Sync, + { + todo!() + } + + #[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") + ))] async fn save_pm_and_mandate<'b>( &self, state: &SessionState, @@ -1602,6 +1622,23 @@ async fn payment_response_update_tracker( } } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +async fn update_payment_method_status_and_ntid( + state: &SessionState, + key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + attempt_status: common_enums::AttemptStatus, + payment_response: Result, + storage_scheme: enums::MerchantStorageScheme, + is_connector_agnostic_mit_enabled: Option, +) -> RouterResult<()> { + todo!() +} + +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] async fn update_payment_method_status_and_ntid( state: &SessionState, key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index d81258d6671..0760cd449fe 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -214,6 +214,28 @@ impl GetTracker, api::PaymentsRetrieveRequest } } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +async fn get_tracker_for_sync< + 'a, + F: Send + Clone, + Op: Operation> + 'a + Send + Sync, +>( + _payment_id: &api::PaymentIdType, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _state: &SessionState, + _request: &api::PaymentsRetrieveRequest, + _operation: Op, + _storage_scheme: enums::MerchantStorageScheme, +) -> RouterResult>> +{ + todo!() +} + +#[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") +))] async fn get_tracker_for_sync< 'a, F: Send + Clone, diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index f948d7faaf9..2a93f8c215d 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -489,7 +489,18 @@ async fn store_bank_details_in_payment_methods( .await .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt customer details")?; + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] let pm_id = generate_id(consts::ID_LENGTH, "pm"); + + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + let pm_id = common_utils::id_type::GlobalPaymentMethodId::generate("random_cell_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to generate GlobalPaymentMethodId")?; + let now = common_utils::date_time::now(); #[cfg(all( any(feature = "v1", feature = "v2"), diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 4dc77211857..8120ccc8089 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1821,6 +1821,10 @@ impl PaymentIntentInterface for KafkaStore { #[async_trait::async_trait] impl PaymentMethodInterface for KafkaStore { + #[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") + ))] async fn find_payment_method( &self, state: &KeyManagerState, @@ -1833,6 +1837,19 @@ impl PaymentMethodInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + async fn find_payment_method( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + payment_method_id: &id_type::GlobalPaymentMethodId, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + self.diesel_store + .find_payment_method(state, key_store, payment_method_id, storage_scheme) + .await + } + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index cf263def41c..6e052076799 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -14,6 +14,10 @@ use crate::{ #[async_trait::async_trait] pub trait PaymentMethodInterface { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] async fn find_payment_method( &self, state: &KeyManagerState, @@ -22,6 +26,15 @@ pub trait PaymentMethodInterface { storage_scheme: MerchantStorageScheme, ) -> CustomResult; + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_payment_method( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + payment_method_id: &id_type::GlobalPaymentMethodId, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult; + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") @@ -231,7 +244,7 @@ mod storage { &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - payment_method_id: &str, + payment_method_id: &id_type::GlobalPaymentMethodId, storage_scheme: MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -251,7 +264,8 @@ mod storage { match storage_scheme { MerchantStorageScheme::PostgresOnly => database_call().await, MerchantStorageScheme::RedisKv => { - let lookup_id = format!("payment_method_{}", payment_method_id); + let lookup_id = + format!("payment_method_{}", payment_method_id.get_string_repr()); let lookup = fallback_reverse_lookup_not_found!( self.get_lookup_by_lookup_id(&lookup_id, storage_scheme) .await, @@ -385,6 +399,38 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + #[instrument(skip_all)] + async fn insert_payment_method( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + payment_method: domain::PaymentMethod, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let payment_method_new = payment_method + .construct_new() + .await + .change_context(errors::StorageError::DecryptionError)?; + + let conn = connection::pg_connection_write(self).await?; + payment_method_new + .insert(&conn) + .await + .map_err(|error| report!(errors::StorageError::from(error)))? + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] #[instrument(skip_all)] async fn insert_payment_method( &self, @@ -591,77 +637,18 @@ mod storage { .await .change_context(errors::StorageError::DecryptionError)?; - let merchant_id = payment_method.merchant_id.clone(); - let customer_id = payment_method.customer_id.clone(); - let key = PartitionKey::MerchantIdCustomerId { - merchant_id: &merchant_id, - customer_id: &customer_id, - }; - let field = format!("payment_method_id_{}", payment_method.get_id()); - let storage_scheme = - Box::pin(decide_storage_scheme::<_, storage_types::PaymentMethod>( - self, - storage_scheme, - Op::Update(key.clone(), &field, payment_method.updated_by.as_deref()), - )) - .await; - let pm = match storage_scheme { - MerchantStorageScheme::PostgresOnly => { - let conn = connection::pg_connection_write(self).await?; - payment_method - .update_with_id( - &conn, - payment_method_update.convert_to_payment_method_update(storage_scheme), - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - } - MerchantStorageScheme::RedisKv => { - let key_str = key.to_string(); - - let p_update: PaymentMethodUpdateInternal = - payment_method_update.convert_to_payment_method_update(storage_scheme); - let updated_payment_method = - p_update.clone().apply_changeset(payment_method.clone()); - - let redis_value = serde_json::to_string(&updated_payment_method) - .change_context(errors::StorageError::SerializationFailed)?; - - let redis_entry = kv::TypedSql { - op: kv::DBOperation::Update { - updatable: kv::Updateable::PaymentMethodUpdate( - kv::PaymentMethodUpdateMems { - orig: payment_method, - update_data: p_update, - }, - ), - }, - }; - - kv_wrapper::<(), _, _>( - self, - KvOperation::::Hset( - (&field, redis_value), - redis_entry, - ), - key, - ) - .await - .map_err(|err| err.to_redis_failed_response(&key_str))? - .try_into_hset() - .change_context(errors::StorageError::KVError)?; - - Ok(updated_payment_method) - } - }?; - - pm.convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError) + let conn = connection::pg_connection_write(self).await?; + payment_method + .update_with_id(&conn, payment_method_update.into()) + .await + .map_err(|error| report!(errors::StorageError::from(error)))? + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) } #[cfg(all( @@ -927,7 +914,7 @@ mod storage { &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - payment_method_id: &str, + payment_method_id: &id_type::GlobalPaymentMethodId, _storage_scheme: MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -1258,6 +1245,10 @@ mod storage { #[async_trait::async_trait] impl PaymentMethodInterface for MockDb { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] async fn find_payment_method( &self, state: &KeyManagerState, @@ -1287,6 +1278,36 @@ impl PaymentMethodInterface for MockDb { } } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + async fn find_payment_method( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + payment_method_id: &id_type::GlobalPaymentMethodId, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let payment_methods = self.payment_methods.lock().await; + let payment_method = payment_methods + .iter() + .find(|pm| pm.get_id() == payment_method_id) + .cloned(); + + match payment_method { + Some(pm) => Ok(pm + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?), + None => Err(errors::StorageError::ValueNotFound( + "cannot find payment method".to_string(), + ) + .into()), + } + } + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 2bafe504dd9..aad5acbbade 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1064,6 +1064,24 @@ impl Payouts { } } +#[cfg(all(feature = "oltp", feature = "v2", feature = "payment_methods_v2",))] +impl PaymentMethods { + pub fn server(state: AppState) -> Scope { + let mut route = web::scope("/v2/payment_methods").app_data(web::Data::new(state)); + route = route + .service(web::resource("").route(web::post().to(create_payment_method_api))) + .service( + web::resource("/create-intent") + .route(web::post().to(create_payment_method_intent_api)), + ) + .service( + web::resource("/{id}/confirm-intent") + .route(web::post().to(confirm_payment_method_intent_api)), + ); + + route + } +} pub struct PaymentMethods; #[cfg(all( diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index e2cb50b0aa7..725d6af81af 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -12,6 +12,11 @@ use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore; use router_env::{instrument, logger, tracing, Flow}; use super::app::{AppState, SessionState}; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use crate::core::payment_methods::{ + create_payment_method, list_customer_payment_method_util, payment_method_intent_confirm, + payment_method_intent_create, +}; use crate::{ core::{ api_locking, errors, @@ -35,6 +40,10 @@ use crate::{ types::api::customers::CustomerRequest, }; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))] pub async fn create_payment_method_api( state: web::Data, @@ -63,6 +72,114 @@ pub async fn create_payment_method_api( .await } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))] +pub async fn create_payment_method_api( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::PaymentMethodsCreate; + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth, req, _| async move { + Box::pin(create_payment_method( + &state, + req, + &auth.merchant_account, + &auth.key_store, + )) + .await + }, + &auth::HeaderAuth(auth::ApiKeyAuth), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))] +pub async fn create_payment_method_intent_api( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::PaymentMethodsCreate; + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth, req, _| async move { + Box::pin(payment_method_intent_create( + &state, + req, + &auth.merchant_account, + &auth.key_store, + )) + .await + }, + &auth::HeaderAuth(auth::ApiKeyAuth), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))] +pub async fn confirm_payment_method_intent_api( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, + path: web::Path, +) -> HttpResponse { + let flow = Flow::PaymentMethodsCreate; + let pm_id = path.into_inner(); + let payload = json_payload.into_inner(); + + 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), + }; + + let inner_payload = payment_methods::PaymentMethodIntentConfirmInternal { + id: pm_id.clone(), + payment_method: payload.payment_method, + payment_method_type: payload.payment_method_type, + client_secret: payload.client_secret.clone(), + customer_id: payload.customer_id.to_owned(), + payment_method_data: payload.payment_method_data.clone(), + }; + + Box::pin(api::server_wrap( + flow, + state, + &req, + inner_payload, + |state, auth, req, _| { + let pm_id = pm_id.clone(); + async move { + Box::pin(payment_method_intent_confirm( + &state, + req.into(), + &auth.merchant_account, + &auth.key_store, + pm_id, + )) + .await + } + }, + &*auth, + api_locking::LockAction::NotApplicable, + )) + .await +} + #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsMigrate))] pub async fn migrate_payment_method_api( state: web::Data, @@ -346,7 +463,7 @@ pub async fn list_customer_payment_method_for_payment( &req, payload, |state, auth, req, _| { - cards::list_customer_payment_method_util( + list_customer_payment_method_util( state, auth.merchant_account, auth.key_store, @@ -411,7 +528,7 @@ pub async fn list_customer_payment_method_api( &req, payload, |state, auth, req, _| { - cards::list_customer_payment_method_util( + list_customer_payment_method_util( state, auth.merchant_account, auth.key_store, diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 16a8ed1ccc4..bcc2046e24e 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -11,6 +11,7 @@ pub mod authentication; pub mod domain; #[cfg(feature = "frm")] pub mod fraud_check; +pub mod payment_methods; pub mod pm_auth; use masking::Secret; pub mod storage; diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index 9262877a385..b3bcfa2a887 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -125,75 +125,7 @@ impl MandateResponseExt for MandateResponse { mandate: storage::Mandate, storage_scheme: storage_enums::MerchantStorageScheme, ) -> RouterResult { - let db = &*state.store; - let payment_method = db - .find_payment_method( - &(state.into()), - &key_store, - &mandate.payment_method_id, - storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; - - let pm = payment_method - .payment_method - .get_required_value("payment_method") - .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) - .attach_printable("payment_method not found")?; - - let card = if pm == storage_enums::PaymentMethod::Card { - // if locker is disabled , decrypt the payment method data - let card_details = if state.conf.locker.locker_enabled { - let card = payment_methods::cards::get_card_from_locker( - state, - &payment_method.customer_id, - &payment_method.merchant_id, - payment_method - .locker_id - .as_ref() - .unwrap_or(&payment_method.id), - ) - .await?; - - payment_methods::transformers::get_card_detail(&payment_method, card) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while getting card details")? - } else { - payment_methods::cards::get_card_details_without_locker_fallback( - &payment_method, - state, - ) - .await? - }; - - Some(MandateCardDetails::from(card_details).into_inner()) - } else { - None - }; - let payment_method_type = payment_method - .payment_method_type - .map(|pmt| pmt.to_string()); - Ok(Self { - mandate_id: mandate.mandate_id, - customer_acceptance: Some(api::payments::CustomerAcceptance { - acceptance_type: if mandate.customer_ip_address.is_some() { - api::payments::AcceptanceType::Online - } else { - api::payments::AcceptanceType::Offline - }, - accepted_at: mandate.customer_accepted_at, - online: Some(api::payments::OnlineMandate { - ip_address: mandate.customer_ip_address, - user_agent: mandate.customer_user_agent.unwrap_or_default(), - }), - }), - card, - status: mandate.mandate_status, - payment_method: pm.to_string(), - payment_method_type, - payment_method_id: mandate.payment_method_id, - }) + todo!() } } diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs index f4d1492fb44..65111815b5c 100644 --- a/crates/router/src/types/api/payment_methods.rs +++ b/crates/router/src/types/api/payment_methods.rs @@ -5,8 +5,8 @@ pub use api_models::payment_methods::{ GetTokenizePayloadRequest, GetTokenizePayloadResponse, ListCountriesCurrenciesRequest, PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate, PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId, - PaymentMethodIntentConfirm, PaymentMethodIntentCreate, PaymentMethodList, - PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse, + PaymentMethodIntentConfirm, PaymentMethodIntentConfirmInternal, PaymentMethodIntentCreate, + PaymentMethodList, PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodMigrate, PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate, PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, @@ -32,6 +32,8 @@ use crate::core::{ errors::{self, RouterResult}, payments::helpers::validate_payment_method_type_against_payment_method, }; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use crate::utils; pub(crate) trait PaymentMethodCreateExt { fn validate(&self) -> RouterResult<()>; @@ -61,15 +63,63 @@ impl PaymentMethodCreateExt for PaymentMethodCreate { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl PaymentMethodCreateExt for PaymentMethodCreate { fn validate(&self) -> RouterResult<()> { - if !validate_payment_method_type_against_payment_method( - self.payment_method, - self.payment_method_type, - ) { - return Err(report!(errors::ApiErrorResponse::InvalidRequestData { - message: "Invalid 'payment_method_type' provided".to_string() - }) - .attach_printable("Invalid payment method type")); - } + utils::when( + !validate_payment_method_type_against_payment_method( + self.payment_method, + self.payment_method_type, + ), + || { + return Err(report!(errors::ApiErrorResponse::InvalidRequestData { + message: "Invalid 'payment_method_type' provided".to_string() + }) + .attach_printable("Invalid payment method type")); + }, + ); + + utils::when( + !PaymentMethodCreate::validate_payment_method_data_against_payment_method( + self.payment_method, + self.payment_method_data.clone(), + ), + || { + return Err(report!(errors::ApiErrorResponse::InvalidRequestData { + message: "Invalid 'payment_method_data' provided".to_string() + }) + .attach_printable("Invalid payment method data")); + }, + ); + Ok(()) + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl PaymentMethodCreateExt for PaymentMethodIntentConfirm { + fn validate(&self) -> RouterResult<()> { + utils::when( + !validate_payment_method_type_against_payment_method( + self.payment_method, + self.payment_method_type, + ), + || { + return Err(report!(errors::ApiErrorResponse::InvalidRequestData { + message: "Invalid 'payment_method_type' provided".to_string() + }) + .attach_printable("Invalid payment method type")); + }, + ); + + utils::when( + !PaymentMethodIntentConfirm::validate_payment_method_data_against_payment_method( + self.payment_method, + self.payment_method_data.clone(), + ), + || { + return Err(report!(errors::ApiErrorResponse::InvalidRequestData { + message: "Invalid 'payment_method_data' provided".to_string() + }) + .attach_printable("Invalid payment method data")); + }, + ); Ok(()) } } diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs new file mode 100644 index 00000000000..933b8e73da8 --- /dev/null +++ b/crates/router/src/types/payment_methods.rs @@ -0,0 +1,103 @@ +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use common_utils::generate_id; + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use crate::{ + consts, + types::{api, domain, storage}, +}; + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +pub trait VaultingInterface { + fn get_vaulting_request_url() -> &'static str; +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +pub trait VaultingDataInterface { + fn get_vaulting_data_key(&self) -> String; +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct VaultFingerprintRequest { + pub data: String, + pub key: String, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct VaultFingerprintResponse { + pub fingerprint_id: String, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct AddVaultRequest { + pub entity_id: common_utils::id_type::MerchantId, + pub vault_id: String, + pub data: D, + pub ttl: i64, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct AddVaultResponse { + pub entity_id: common_utils::id_type::MerchantId, + pub vault_id: String, + pub fingerprint_id: Option, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct AddVault; + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct GetVaultFingerprint; + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +impl VaultingInterface for AddVault { + fn get_vaulting_request_url() -> &'static str { + consts::ADD_VAULT_REQUEST_URL + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +impl VaultingInterface for GetVaultFingerprint { + fn get_vaulting_request_url() -> &'static str { + consts::VAULT_FINGERPRINT_REQUEST_URL + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +impl VaultingDataInterface for api::PaymentMethodCreateData { + fn get_vaulting_data_key(&self) -> String { + match &self { + api::PaymentMethodCreateData::Card(card) => card.card_number.to_string(), + } + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub struct PaymentMethodClientSecret; + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +impl PaymentMethodClientSecret { + pub fn generate(payment_method_id: &common_utils::id_type::GlobalPaymentMethodId) -> String { + todo!() + } +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub struct SavedPMLPaymentsInfo { + pub payment_intent: storage::PaymentIntent, + pub business_profile: Option, + pub requires_cvv: bool, + pub off_session_payment_flag: bool, + pub is_connector_agnostic_mit_enabled: bool, +} diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 5bbfd8483cd..34c5714e126 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -19,6 +19,10 @@ pub enum PaymentTokenKind { Permanent, } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct CardTokenData { pub payment_method_id: Option, @@ -27,6 +31,14 @@ pub struct CardTokenData { pub network_token_locker_id: Option, } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct CardTokenData { + pub payment_method_id: Option, + pub locker_id: Option, + pub token: String, +} + #[derive(Debug, Clone, serde::Serialize, Default, serde::Deserialize)] pub struct PaymentMethodDataWithId { pub payment_method: Option, @@ -58,6 +70,10 @@ pub enum PaymentTokenData { } impl PaymentTokenData { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] pub fn permanent_card( payment_method_id: Option, locker_id: Option, @@ -72,6 +88,19 @@ impl PaymentTokenData { }) } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] + pub fn permanent_card( + payment_method_id: Option, + locker_id: Option, + token: String, + ) -> Self { + Self::PermanentCard(CardTokenData { + payment_method_id, + locker_id, + token, + }) + } + pub fn temporary_generic(token: String) -> Self { Self::TemporaryGeneric(GenericTokenData { token }) } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 1442f5b8d48..a6f6d7c4c5a 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -129,19 +129,7 @@ impl domain::PaymentMethod, ), ) -> Self { - Self { - merchant_id: item.merchant_id.to_owned(), - customer_id: item.customer_id.to_owned(), - payment_method_id: item.get_id().clone(), - payment_method: item.payment_method, - payment_method_type: item.payment_method_type, - payment_method_data: card_details.map(payment_methods::PaymentMethodResponseData::Card), - recurring_enabled: false, - metadata: item.metadata, - created: Some(item.created_at), - last_used_at: None, - client_secret: item.client_secret, - } + todo!() } } diff --git a/crates/router/src/workflows/payment_method_status_update.rs b/crates/router/src/workflows/payment_method_status_update.rs index abc30074cda..124417e3355 100644 --- a/crates/router/src/workflows/payment_method_status_update.rs +++ b/crates/router/src/workflows/payment_method_status_update.rs @@ -14,6 +14,10 @@ pub struct PaymentMethodStatusUpdateWorkflow; #[async_trait::async_trait] impl ProcessTrackerWorkflow for PaymentMethodStatusUpdateWorkflow { + #[cfg(all( + any(feature = "v2", feature = "v1"), + not(feature = "payment_methods_v2") + ))] async fn execute_workflow<'a>( &'a self, state: &'a SessionState, diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index 6a796386777..cadb78944ee 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -437,7 +437,7 @@ impl UniqueConstraints for diesel_models::PaymentMethod { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl UniqueConstraints for diesel_models::PaymentMethod { fn unique_constraints(&self) -> Vec { - vec![format!("paymentmethod_{}", self.id)] + vec![self.id.get_string_repr()] } fn table_name(&self) -> &str { "PaymentMethod" From 0a0c93e102dc3183627db3b871370d7b5661f151 Mon Sep 17 00:00:00 2001 From: Riddhiagrawal001 <50551695+Riddhiagrawal001@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:24:42 +0530 Subject: [PATCH 12/48] feat(disputes): add support for disputes aggregate (#5896) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/disputes.rs | 8 +++ crates/api_models/src/events/dispute.rs | 9 ++- crates/common_enums/src/enums.rs | 1 + crates/router/src/core/disputes.rs | 31 ++++++++++ crates/router/src/db/dispute.rs | 71 ++++++++++++++++++++++ crates/router/src/db/kafka_store.rs | 11 ++++ crates/router/src/routes/app.rs | 7 +++ crates/router/src/routes/disputes.rs | 67 +++++++++++++++++++- crates/router/src/routes/lock_utils.rs | 1 + crates/router/src/types/storage/dispute.rs | 41 +++++++++++++ crates/router_env/src/logger/types.rs | 2 + 11 files changed, 247 insertions(+), 2 deletions(-) diff --git a/crates/api_models/src/disputes.rs b/crates/api_models/src/disputes.rs index 5c2a2cc03d2..a1340b5aa5a 100644 --- a/crates/api_models/src/disputes.rs +++ b/crates/api_models/src/disputes.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use masking::{Deserialize, Serialize}; use time::PrimitiveDateTime; use utoipa::ToSchema; @@ -208,3 +210,9 @@ pub struct DeleteEvidenceRequest { /// Evidence Type to be deleted pub evidence_type: EvidenceType, } + +#[derive(Clone, Debug, serde::Serialize)] +pub struct DisputesAggregateResponse { + /// Different status of disputes with their count + pub status_with_count: HashMap, +} diff --git a/crates/api_models/src/events/dispute.rs b/crates/api_models/src/events/dispute.rs index 1ea6f4e8552..57f91330cc9 100644 --- a/crates/api_models/src/events/dispute.rs +++ b/crates/api_models/src/events/dispute.rs @@ -1,7 +1,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; use super::{ - DeleteEvidenceRequest, DisputeResponse, DisputeResponsePaymentsRetrieve, SubmitEvidenceRequest, + DeleteEvidenceRequest, DisputeResponse, DisputeResponsePaymentsRetrieve, + DisputesAggregateResponse, SubmitEvidenceRequest, }; impl ApiEventMetric for SubmitEvidenceRequest { @@ -32,3 +33,9 @@ impl ApiEventMetric for DeleteEvidenceRequest { }) } } + +impl ApiEventMetric for DisputesAggregateResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 653275bd560..31cb43669b2 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1843,6 +1843,7 @@ pub enum DisputeStage { serde::Serialize, strum::Display, strum::EnumString, + strum::EnumIter, ToSchema, )] #[router_derive::diesel_enum(storage_type = "db_enum")] diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 8835f5f95a8..ff5936fc296 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -3,6 +3,9 @@ use common_utils::ext_traits::{Encode, ValueExt}; use error_stack::ResultExt; use router_env::{instrument, tracing}; pub mod transformers; +use std::collections::HashMap; + +use strum::IntoEnumIterator; use super::{ errors::{self, ConnectorErrorExt, RouterResponse, StorageErrorExt}, @@ -507,3 +510,31 @@ pub async fn delete_evidence( })?; Ok(services::ApplicationResponse::StatusOk) } + +#[instrument(skip(state))] +pub async fn get_aggregates_for_disputes( + state: SessionState, + merchant: domain::MerchantAccount, + profile_id_list: Option>, + time_range: api::TimeRange, +) -> RouterResponse { + let db = state.store.as_ref(); + let dispute_status_with_count = db + .get_dispute_status_with_count(merchant.get_id(), profile_id_list, &time_range) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to retrieve disputes aggregate")?; + + let mut status_map: HashMap = + dispute_status_with_count.into_iter().collect(); + + for status in storage_enums::DisputeStatus::iter() { + status_map.entry(status).or_default(); + } + + Ok(services::ApplicationResponse::Json( + dispute_models::DisputesAggregateResponse { + status_with_count: status_map, + }, + )) +} diff --git a/crates/router/src/db/dispute.rs b/crates/router/src/db/dispute.rs index abe469dc32a..1d45cfd405f 100644 --- a/crates/router/src/db/dispute.rs +++ b/crates/router/src/db/dispute.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use error_stack::report; use router_env::{instrument, tracing}; @@ -45,6 +47,13 @@ pub trait DisputeInterface { this: storage::Dispute, dispute: storage::DisputeUpdate, ) -> CustomResult; + + async fn get_dispute_status_with_count( + &self, + merchant_id: &common_utils::id_type::MerchantId, + profile_id_list: Option>, + time_range: &api_models::payments::TimeRange, + ) -> CustomResult, errors::StorageError>; } #[async_trait::async_trait] @@ -126,6 +135,24 @@ impl DisputeInterface for Store { .await .map_err(|error| report!(errors::StorageError::from(error))) } + + #[instrument(skip_all)] + async fn get_dispute_status_with_count( + &self, + merchant_id: &common_utils::id_type::MerchantId, + profile_id_list: Option>, + time_range: &api_models::payments::TimeRange, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + storage::Dispute::get_dispute_status_with_count( + &conn, + merchant_id, + profile_id_list, + time_range, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } } #[async_trait::async_trait] @@ -358,6 +385,50 @@ impl DisputeInterface for MockDb { Ok(dispute_to_update.clone()) } + + async fn get_dispute_status_with_count( + &self, + merchant_id: &common_utils::id_type::MerchantId, + profile_id_list: Option>, + time_range: &api_models::payments::TimeRange, + ) -> CustomResult, errors::StorageError> { + let locked_disputes = self.disputes.lock().await; + + let filtered_disputes_data = locked_disputes + .iter() + .filter(|d| { + d.merchant_id == *merchant_id + && d.created_at >= time_range.start_time + && time_range + .end_time + .as_ref() + .map(|received_end_time| received_end_time >= &d.created_at) + .unwrap_or(true) + && profile_id_list + .as_ref() + .zip(d.profile_id.as_ref()) + .map(|(received_profile_list, received_profile_id)| { + received_profile_list.contains(received_profile_id) + }) + .unwrap_or(true) + }) + .cloned() + .collect::>(); + + Ok(filtered_disputes_data + .into_iter() + .fold( + HashMap::new(), + |mut acc: HashMap, value| { + acc.entry(value.dispute_status) + .and_modify(|value| *value += 1) + .or_insert(1); + acc + }, + ) + .into_iter() + .collect::>()) + } } #[cfg(test)] diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 8120ccc8089..394c48c1cd8 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -629,6 +629,17 @@ impl DisputeInterface for KafkaStore { .find_disputes_by_merchant_id_payment_id(merchant_id, payment_id) .await } + + async fn get_dispute_status_with_count( + &self, + merchant_id: &id_type::MerchantId, + profile_id_list: Option>, + time_range: &api_models::payments::TimeRange, + ) -> CustomResult, errors::StorageError> { + self.diesel_store + .get_dispute_status_with_count(merchant_id, profile_id_list, time_range) + .await + } } #[async_trait::async_trait] diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index aad5acbbade..f2be2f7bf50 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1495,6 +1495,13 @@ impl Disputes { web::resource("/accept/{dispute_id}") .route(web::post().to(disputes::accept_dispute)), ) + .service( + web::resource("/aggregate").route(web::get().to(disputes::get_disputes_aggregate)), + ) + .service( + web::resource("/profile/aggregate") + .route(web::get().to(disputes::get_disputes_aggregate_profile)), + ) .service( web::resource("/evidence") .route(web::post().to(disputes::submit_dispute_evidence)) diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index 98ba33a64b4..5d8f7120f40 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -11,7 +11,7 @@ use super::app::AppState; use crate::{ core::disputes, services::{api, authentication as auth}, - types::api::disputes as dispute_types, + types::api::{disputes as dispute_types, payments::TimeRange}, }; /// Disputes - Retrieve Dispute @@ -408,3 +408,68 @@ pub async fn delete_dispute_evidence( )) .await } + +#[instrument(skip_all, fields(flow = ?Flow::DisputesAggregate))] +pub async fn get_disputes_aggregate( + state: web::Data, + req: HttpRequest, + query_param: web::Query, +) -> HttpResponse { + let flow = Flow::DisputesAggregate; + let query_param = query_param.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + query_param, + |state, auth, req, _| { + disputes::get_aggregates_for_disputes(state, auth.merchant_account, None, req) + }, + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: Permission::DisputeRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::DisputesAggregate))] +pub async fn get_disputes_aggregate_profile( + state: web::Data, + req: HttpRequest, + query_param: web::Query, +) -> HttpResponse { + let flow = Flow::DisputesAggregate; + let query_param = query_param.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + query_param, + |state, auth, req, _| { + disputes::get_aggregates_for_disputes( + state, + auth.merchant_account, + auth.profile_id.map(|profile_id| vec![profile_id]), + req, + ) + }, + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: Permission::DisputeRead, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index e63b00cd9bf..774e6c75bb4 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -175,6 +175,7 @@ impl From for ApiIdentifier { | Flow::DisputesEvidenceSubmit | Flow::AttachDisputeEvidence | Flow::RetrieveDisputeEvidence + | Flow::DisputesAggregate | Flow::DeleteDisputeEvidence => Self::Disputes, Flow::CardsInfo => Self::CardsInfo, diff --git a/crates/router/src/types/storage/dispute.rs b/crates/router/src/types/storage/dispute.rs index 2728b29bc07..79b9a90343e 100644 --- a/crates/router/src/types/storage/dispute.rs +++ b/crates/router/src/types/storage/dispute.rs @@ -14,6 +14,13 @@ pub trait DisputeDbExt: Sized { merchant_id: &common_utils::id_type::MerchantId, dispute_list_constraints: api_models::disputes::DisputeListConstraints, ) -> CustomResult, errors::DatabaseError>; + + async fn get_dispute_status_with_count( + conn: &PgPooledConn, + merchant_id: &common_utils::id_type::MerchantId, + profile_id_list: Option>, + time_range: &api_models::payments::TimeRange, + ) -> CustomResult, errors::DatabaseError>; } #[async_trait::async_trait] @@ -72,4 +79,38 @@ impl DisputeDbExt for Dispute { .change_context(errors::DatabaseError::NotFound) .attach_printable_lazy(|| "Error filtering records by predicate") } + + async fn get_dispute_status_with_count( + conn: &PgPooledConn, + merchant_id: &common_utils::id_type::MerchantId, + profile_id_list: Option>, + time_range: &api_models::payments::TimeRange, + ) -> CustomResult, errors::DatabaseError> { + let mut query = ::table() + .group_by(dsl::dispute_status) + .select((dsl::dispute_status, diesel::dsl::count_star())) + .filter(dsl::merchant_id.eq(merchant_id.to_owned())) + .into_boxed(); + + if let Some(profile_id) = profile_id_list { + query = query.filter(dsl::profile_id.eq_any(profile_id)); + } + + query = query.filter(dsl::created_at.ge(time_range.start_time)); + + query = match time_range.end_time { + Some(ending_at) => query.filter(dsl::created_at.le(ending_at)), + None => query, + }; + + logger::debug!(query = %diesel::debug_query::(&query).to_string()); + + db_metrics::track_database_call::<::Table, _, _>( + query.get_results_async::<(common_enums::DisputeStatus, i64)>(conn), + db_metrics::DatabaseOperation::Count, + ) + .await + .change_context(errors::DatabaseError::NotFound) + .attach_printable_lazy(|| "Error filtering records by predicate") + } } diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index d815325a814..310f5ccecbf 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -290,6 +290,8 @@ pub enum Flow { AttachDisputeEvidence, /// Delete Dispute Evidence flow DeleteDisputeEvidence, + /// Disputes aggregate flow + DisputesAggregate, /// Retrieve Dispute Evidence flow RetrieveDisputeEvidence, /// Invalidate cache flow From 2bc8756e061e9e0e705ce2c6b533c3583f3efa8b Mon Sep 17 00:00:00 2001 From: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:24:55 +0530 Subject: [PATCH 13/48] docs: add openapi docs for customers v2 (#5926) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 221 +++++++++++++++++++++++++ crates/openapi/src/openapi_v2.rs | 7 + crates/openapi/src/routes/customers.rs | 111 +++++++++++++ 3 files changed, 339 insertions(+) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 117c9d2e9e7..9db2fcfdfdc 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1432,6 +1432,227 @@ } ] } + }, + "/v2/customers": { + "post": { + "tags": [ + "Customers" + ], + "summary": "Creates a customer object and stores the customer details to be reused for future payments.", + "description": "Incase the customer already exists in the system, this API will respond with the customer details.", + "operationId": "Create a Customer", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerRequest" + }, + "examples": { + "Create a customer with name and email": { + "value": { + "email": "guest@example.com", + "name": "John Doe" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Customer Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "400": { + "description": "Invalid data" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/v2/customers/{id}": { + "get": { + "tags": [ + "Customers" + ], + "summary": "Customers - Retrieve", + "description": "Retrieves a customer's details.", + "operationId": "Retrieve a Customer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The unique identifier for the Customer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Customer Retrieved", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "404": { + "description": "Customer was not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "ephemeral_key": [] + } + ] + }, + "post": { + "tags": [ + "Customers" + ], + "summary": "Customers - Update", + "description": "Updates the customer's details in a customer object.", + "operationId": "Update a Customer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The unique identifier for the Customer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerRequest" + }, + "examples": { + "Update name and email of a customer": { + "value": { + "email": "guest@example.com", + "name": "John Doe" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Customer was Updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + }, + "404": { + "description": "Customer was not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "delete": { + "tags": [ + "Customers" + ], + "summary": "Customers - Delete", + "description": "Delete a customer record.", + "operationId": "Delete a Customer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The unique identifier for the Customer", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Customer was Deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerDeleteResponse" + } + } + } + }, + "404": { + "description": "Customer was not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/v2/customers/list": { + "post": { + "tags": [ + "Customers" + ], + "summary": "Customers - List", + "description": "Lists all the customers for a particular merchant id.", + "operationId": "List all Customers for a Merchant", + "responses": { + "200": { + "description": "Customers retrieved", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerResponse" + } + } + } + } + }, + "400": { + "description": "Invalid Data" + } + }, + "security": [ + { + "api_key": [] + } + ] + } } }, "components": { diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index f2a920cdb92..8a21110ae83 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -109,6 +109,13 @@ Never share your secret api keys. Keep them guarded and secure. routes::api_keys::api_key_retrieve, routes::api_keys::api_key_update, routes::api_keys::api_key_revoke, + + //Routes for customers + routes::customers::customers_create, + routes::customers::customers_retrieve, + routes::customers::customers_update, + routes::customers::customers_delete, + routes::customers::customers_list, ), components(schemas( common_utils::types::MinorUnit, diff --git a/crates/openapi/src/routes/customers.rs b/crates/openapi/src/routes/customers.rs index 2794ee73107..26140252ebd 100644 --- a/crates/openapi/src/routes/customers.rs +++ b/crates/openapi/src/routes/customers.rs @@ -23,6 +23,7 @@ operation_id = "Create a Customer", security(("api_key" = [])) )] +#[cfg(feature = "v1")] pub async fn customers_create() {} /// Customers - Retrieve @@ -40,6 +41,7 @@ pub async fn customers_create() {} operation_id = "Retrieve a Customer", security(("api_key" = []), ("ephemeral_key" = [])) )] +#[cfg(feature = "v1")] pub async fn customers_retrieve() {} /// Customers - Update @@ -66,6 +68,7 @@ pub async fn customers_retrieve() {} operation_id = "Update a Customer", security(("api_key" = [])) )] +#[cfg(feature = "v1")] pub async fn customers_update() {} /// Customers - Delete @@ -83,6 +86,7 @@ pub async fn customers_update() {} operation_id = "Delete a Customer", security(("api_key" = [])) )] +#[cfg(feature = "v1")] pub async fn customers_delete() {} /// Customers - List @@ -99,4 +103,111 @@ pub async fn customers_delete() {} operation_id = "List all Customers for a Merchant", security(("api_key" = [])) )] +#[cfg(feature = "v1")] +pub async fn customers_list() {} + +/// Creates a customer object and stores the customer details to be reused for future payments. +/// Incase the customer already exists in the system, this API will respond with the customer details. +#[utoipa::path( + post, + path = "/v2/customers", + request_body ( + content = CustomerRequest, + examples (( "Create a customer with name and email" =( + value =json!( { + "email": "guest@example.com", + "name": "John Doe" + }) + ))) + ), + responses( + (status = 200, description = "Customer Created", body = CustomerResponse), + (status = 400, description = "Invalid data") + + ), + tag = "Customers", + operation_id = "Create a Customer", + security(("api_key" = [])) +)] +#[cfg(feature = "v2")] +pub async fn customers_create() {} + +/// Customers - Retrieve +/// +/// Retrieves a customer's details. +#[utoipa::path( + get, + path = "/v2/customers/{id}", + params (("id" = String, Path, description = "The unique identifier for the Customer")), + responses( + (status = 200, description = "Customer Retrieved", body = CustomerResponse), + (status = 404, description = "Customer was not found") + ), + tag = "Customers", + operation_id = "Retrieve a Customer", + security(("api_key" = []), ("ephemeral_key" = [])) +)] +#[cfg(feature = "v2")] +pub async fn customers_retrieve() {} + +/// Customers - Update +/// +/// Updates the customer's details in a customer object. +#[utoipa::path( + post, + path = "/v2/customers/{id}", + request_body ( + content = CustomerRequest, + examples (( "Update name and email of a customer" =( + value =json!( { + "email": "guest@example.com", + "name": "John Doe" + }) + ))) + ), + params (("id" = String, Path, description = "The unique identifier for the Customer")), + responses( + (status = 200, description = "Customer was Updated", body = CustomerResponse), + (status = 404, description = "Customer was not found") + ), + tag = "Customers", + operation_id = "Update a Customer", + security(("api_key" = [])) +)] +#[cfg(feature = "v2")] +pub async fn customers_update() {} + +/// Customers - Delete +/// +/// Delete a customer record. +#[utoipa::path( + delete, + path = "/v2/customers/{id}", + params (("id" = String, Path, description = "The unique identifier for the Customer")), + responses( + (status = 200, description = "Customer was Deleted", body = CustomerDeleteResponse), + (status = 404, description = "Customer was not found") + ), + tag = "Customers", + operation_id = "Delete a Customer", + security(("api_key" = [])) +)] +#[cfg(feature = "v2")] +pub async fn customers_delete() {} + +/// Customers - List +/// +/// Lists all the customers for a particular merchant id. +#[utoipa::path( + post, + path = "/v2/customers/list", + responses( + (status = 200, description = "Customers retrieved", body = Vec), + (status = 400, description = "Invalid Data"), + ), + tag = "Customers", + operation_id = "List all Customers for a Merchant", + security(("api_key" = [])) +)] +#[cfg(feature = "v2")] pub async fn customers_list() {} From b2364414edab6d44e276f2133bc988610693f155 Mon Sep 17 00:00:00 2001 From: likhinbopanna <131246334+likhinbopanna@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:04:27 +0530 Subject: [PATCH 14/48] ci(postman): Add browser info for adyen postman collection (#5927) --- .../Flow Testcases/Happy Cases/.meta.json | 26 +- .../Payments - Create/request.json | 15 +- .../.meta.json | 0 .../Payments - Create/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 15 +- .../Payments - Create/response.json | 0 .../Payments - Retrieve-copy/.event.meta.json | 0 .../Payments - Retrieve-copy/event.test.js | 0 .../Payments - Retrieve-copy/request.json | 0 .../Payments - Retrieve-copy/response.json | 0 .../Payments - Retrieve/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve/response.json | 0 .../Refunds - Create-copy/.event.meta.json | 0 .../Refunds - Create-copy/event.test.js | 0 .../Refunds - Create-copy/request.json | 0 .../Refunds - Create-copy}/response.json | 0 .../Refunds - Create/.event.meta.json | 0 .../Refunds - Create/event.test.js | 0 .../Refunds - Create/request.json | 0 .../Refunds - Create}/response.json | 0 .../Refunds - Retrieve-copy/.event.meta.json | 0 .../Refunds - Retrieve-copy/event.test.js | 0 .../Refunds - Retrieve-copy/request.json | 0 .../Refunds - Retrieve-copy}/response.json | 0 .../Refunds - Retrieve/.event.meta.json | 0 .../Refunds - Retrieve/event.test.js | 0 .../Refunds - Retrieve/request.json | 0 .../Refunds - Retrieve}/response.json | 0 .../.meta.json | 0 .../Payments - Confirm}/.event.meta.json | 0 .../Payments - Confirm/event.test.js | 0 .../Payments - Confirm/request.json | 0 .../Payments - Confirm}/response.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 0 .../Payments - Create}/response.json | 0 .../Payments - Retrieve/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve}/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.meta.json | 0 .../Payments - Confirm}/.event.meta.json | 0 .../Payments - Confirm/event.test.js | 0 .../Payments - Confirm/request.json | 0 .../Payments - Confirm}/response.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 0 .../Payments - Create}/response.json | 0 .../Payments - Retrieve}/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.meta.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 19 +- .../Payments - Create}/response.json | 0 .../.event.meta.json | 0 .../Payments - Retrieve-copy/event.test.js | 0 .../Payments - Retrieve-copy}/request.json | 0 .../Payments - Retrieve-copy}/response.json | 0 .../Payments - Retrieve}/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.event.meta.json | 0 .../Recurring Payments - Create/event.test.js | 0 .../Recurring Payments - Create/request.json | 0 .../response.json | 0 .../Refunds - Create Copy}/.event.meta.json | 0 .../Refunds - Create Copy/event.test.js | 0 .../Refunds - Create Copy/request.json | 0 .../Refunds - Create Copy}/response.json | 0 .../Refunds - Retrieve Copy}/.event.meta.json | 0 .../Refunds - Retrieve Copy/event.test.js | 0 .../Refunds - Retrieve Copy/request.json | 0 .../Refunds - Retrieve Copy}/response.json | 0 .../.meta.json | 0 .../Payments - Confirm}/.event.meta.json | 0 .../Payments - Confirm/event.test.js | 0 .../Payments - Confirm/request.json | 0 .../Payments - Confirm}/response.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 0 .../Payments - Create}/response.json | 0 .../Payments - Retrieve}/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve}/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../Payments - Confirm/event.test.js | 103 -- .../Payments - Confirm/request.json | 83 -- .../Payments - Create/request.json | 88 -- .../.meta.json | 0 .../Payments - Confirm}/.event.meta.json | 0 .../Payments - Confirm/event.test.js | 0 .../Payments - Confirm/request.json | 11 + .../Payments - Confirm}/response.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 0 .../Payments - Create}/response.json | 0 .../Payments - Retrieve}/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.meta.json | 0 .../Payments - Confirm}/.event.meta.json | 0 .../Payments - Confirm/event.test.js | 0 .../Payments - Confirm/request.json | 0 .../Payments - Confirm}/response.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 0 .../Payments - Create}/response.json | 0 .../Payments - Retrieve}/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.meta.json | 0 .../.event.meta.json | 0 .../event.test.js | 0 .../request.json | 0 .../response.json | 0 .../Payments - Create/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 15 +- .../Payments - Create}/response.json | 0 .../Payments - Retrieve Copy/.event.meta.json | 0 .../Payments - Retrieve Copy/event.test.js | 0 .../Payments - Retrieve Copy/request.json | 0 .../Payments - Retrieve Copy}/response.json | 0 .../Refunds - Create/.event.meta.json | 0 .../Refunds - Create/event.test.js | 0 .../Refunds - Create/request.json | 0 .../Refunds - Create}/response.json | 0 .../Refunds - Retrieve/.event.meta.json | 0 .../Refunds - Retrieve/event.test.js | 0 .../Refunds - Retrieve/request.json | 0 .../Refunds - Retrieve}/response.json | 0 .../.event.meta.json | 0 .../event.test.js | 0 .../Save card payments - Confirm/request.json | 23 +- .../response.json | 0 .../.event.meta.json | 0 .../Save card payments - Create/event.test.js | 0 .../Save card payments - Create/request.json | 0 .../response.json | 0 .../.meta.json | 7 - .../Payments - Create/event.test.js | 71 -- .../Payments - Retrieve/event.test.js | 71 -- .../.meta.json | 0 .../.event.meta.json | 0 .../event.test.js | 0 .../request.json | 0 .../response.json | 0 .../Payments - Create/.event.meta.json | 0 .../Payments - Create/event.prerequest.js | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 15 +- .../Payments - Create/response.json | 0 .../Payments - Retrieve/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.event.meta.json | 0 .../event.test.js | 0 .../Save card payments - Confirm/request.json | 24 +- .../response.json | 0 .../.event.meta.json | 0 .../Save card payments - Create/event.test.js | 0 .../Save card payments - Create/request.json | 0 .../response.json | 0 .../.meta.json | 0 .../.event.meta.json | 0 .../event.test.js | 0 .../request.json | 0 .../response.json | 0 .../Payments - Create/.event.meta.json | 0 .../Payments - Create/event.prerequest.js | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 15 +- .../Payments - Create}/response.json | 0 .../Payments - Retrieve/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../.event.meta.json | 0 .../event.test.js | 0 .../Save card payments - Confirm/request.json | 22 +- .../response.json | 0 .../.event.meta.json | 0 .../Save card payments - Create/event.test.js | 0 .../Save card payments - Create/request.json | 0 .../response.json | 0 .../Payments - Confirm/request.json | 13 +- .../Payments - Create/request.json | 4 +- .../.meta.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 21 +- .../Payments - Create}/response.json | 0 .../Payments - Retrieve}/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../Payments - Confirm/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../.meta.json | 0 .../Payments - Create/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 23 +- .../Payments - Create}/response.json | 0 .../Payments - Retrieve/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve}/response.json | 0 .../Refunds - Create}/.event.meta.json | 0 .../Refunds - Create/event.test.js | 0 .../Refunds - Create/request.json | 0 .../Refunds - Create}/response.json | 0 .../Refunds - Retrieve}/.event.meta.json | 0 .../Refunds - Retrieve/event.test.js | 0 .../Refunds - Retrieve/request.json | 0 .../Refunds - Retrieve}/response.json | 0 .../.meta.json | 0 .../Payments - Create}/.event.meta.json | 0 .../Payments - Create/event.test.js | 0 .../Payments - Create/request.json | 15 +- .../Payments - Create}/response.json | 0 .../.event.meta.json | 0 .../Payments - Retrieve-copy/event.test.js | 0 .../Payments - Retrieve-copy}/request.json | 0 .../Payments - Retrieve-copy}/response.json | 0 .../Payments - Retrieve/.event.meta.json | 0 .../Payments - Retrieve/event.test.js | 0 .../Payments - Retrieve/request.json | 0 .../Payments - Retrieve/response.json | 0 .../.event.meta.json | 0 .../Recurring Payments - Create/event.test.js | 0 .../Recurring Payments - Create/request.json | 0 .../response.json | 0 .../Payments - Retrieve/.event.meta.json | 3 - .../Payments - Retrieve/request.json | 28 - .../Payments - Retrieve/response.json | 1 - .../Refunds - Create/.event.meta.json | 3 - .../Refunds - Create/response.json | 1 - .../Refunds - Retrieve/.event.meta.json | 3 - .../Refunds - Retrieve/response.json | 1 - .../QuickStart/Payments - Create/request.json | 15 +- .../request.json | 11 + .../request.json | 11 + .../request.json | 11 + .../request.json | 11 + .../Payments - Create/request.json | 21 +- .../ACH Payouts - Create/request.json | 13 +- .../Bacs Payouts - Create/request.json | 13 +- .../Payments - Confirm/request.json | 13 +- .../Payments - Create/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../Payments - Create/request.json | 15 +- .../adyen_uk.postman_collection.json | 1003 +++++------------ 271 files changed, 721 insertions(+), 1275 deletions(-) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario10-Partial refund}/Payments - Create/request.json (80%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario10-Partial refund}/Payments - Create/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Retrieve-copy/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Retrieve-copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Retrieve-copy/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario10-Partial refund}/Payments - Retrieve-copy/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario10-Partial refund}/Payments - Retrieve/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Create-copy/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Create-copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Create-copy/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment/Recurring Payments - Create => Scenario10-Partial refund/Refunds - Create-copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Payments - Create => Scenario10-Partial refund/Refunds - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Retrieve-copy/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Retrieve-copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Retrieve-copy/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Payments - Retrieve-copy => Scenario10-Partial refund/Refunds - Retrieve-copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario10-Partial refund}/Refunds - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Payments - Retrieve => Scenario10-Partial refund/Refunds - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort => Scenario11-Bank Redirect-sofort}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment/Payments - Create => Scenario11-Bank Redirect-sofort/Payments - Confirm}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort => Scenario11-Bank Redirect-sofort}/Payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort => Scenario11-Bank Redirect-sofort}/Payments - Confirm/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Refunds - Create-copy => Scenario11-Bank Redirect-sofort/Payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy => Scenario11-Bank Redirect-sofort/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort => Scenario11-Bank Redirect-sofort}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort => Scenario11-Bank Redirect-sofort}/Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Refunds - Create => Scenario11-Bank Redirect-sofort/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario11-Bank Redirect-sofort}/Payments - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort => Scenario11-Bank Redirect-sofort}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy => Scenario11-Bank Redirect-sofort/Payments - Retrieve}/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Refunds - Retrieve-copy => Scenario11-Bank Redirect-sofort/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario12-Bank Redirect-eps}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment/Recurring Payments - Create => Scenario12-Bank Redirect-eps/Payments - Confirm}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario12-Bank Redirect-eps}/Payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario12-Bank Redirect-eps}/Payments - Confirm/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund/Refunds - Retrieve => Scenario12-Bank Redirect-eps/Payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Confirm => Scenario12-Bank Redirect-eps/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario12-Bank Redirect-eps}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario12-Bank Redirect-eps}/Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Confirm => Scenario12-Bank Redirect-eps/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Create => Scenario12-Bank Redirect-eps/Payments - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario12-Bank Redirect-eps}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario12-Bank Redirect-eps}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Create => Scenario12-Bank Redirect-eps/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Retrieve => Scenario13-Refund recurring payment/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario13-Refund recurring payment}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Payments - Create/request.json (82%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Retrieve => Scenario13-Refund recurring payment/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps/Payments - Confirm => Scenario13-Refund recurring payment/Payments - Retrieve-copy}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario13-Refund recurring payment}/Payments - Retrieve-copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario12-Bank Redirect-sofort/Payments - Retrieve => Scenario13-Refund recurring payment/Payments - Retrieve-copy}/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps/Payments - Confirm => Scenario13-Refund recurring payment/Payments - Retrieve-copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps/Payments - Create => Scenario13-Refund recurring payment/Payments - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario13-Refund recurring payment}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps => Scenario13-Refund recurring payment}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps/Payments - Create => Scenario13-Refund recurring payment/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps/Payments - Retrieve => Scenario13-Refund recurring payment/Recurring Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Recurring Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Recurring Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario13-Bank Redirect-eps/Payments - Retrieve => Scenario13-Refund recurring payment/Recurring Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Create => Scenario13-Refund recurring payment/Refunds - Create Copy}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Refunds - Create Copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Refunds - Create Copy/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Create => Scenario13-Refund recurring payment/Refunds - Create Copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Retrieve-copy => Scenario13-Refund recurring payment/Refunds - Retrieve Copy}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Refunds - Retrieve Copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario13-Refund recurring payment}/Refunds - Retrieve Copy/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Retrieve-copy => Scenario13-Refund recurring payment/Refunds - Retrieve Copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay => Scenario14-Bank debit-ach}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Retrieve => Scenario14-Bank debit-ach/Payments - Confirm}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario14-Bank debit-ach}/Payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario14-Bank debit-ach}/Payments - Confirm/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Retrieve => Scenario14-Bank debit-ach/Payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Recurring Payments - Create => Scenario14-Bank debit-ach/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay => Scenario14-Bank debit-ach}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario14-Bank debit-ach}/Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Recurring Payments - Create => Scenario14-Bank debit-ach/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Refunds - Create Copy => Scenario14-Bank debit-ach/Payments - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario14-Bank debit-ach}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Payments - Retrieve-copy => Scenario14-Bank debit-ach/Payments - Retrieve}/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Refunds - Create Copy => Scenario14-Bank debit-ach/Payments - Retrieve}/response.json (100%) delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/event.test.js delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/request.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/request.json rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario15-Bank debit-Bacs}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Refunds - Retrieve Copy => Scenario15-Bank debit-Bacs/Payments - Confirm}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario15-Bank debit-Bacs}/Payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario15-Bank debit-Bacs}/Payments - Confirm/request.json (80%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment/Refunds - Retrieve Copy => Scenario15-Bank debit-Bacs/Payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay/Payments - Confirm => Scenario15-Bank debit-Bacs/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario15-Bank debit-Bacs}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario15-Bank debit-Bacs}/Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay/Payments - Confirm => Scenario15-Bank debit-Bacs/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay/Payments - Create => Scenario15-Bank debit-Bacs/Payments - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario15-Bank debit-Bacs}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario15-Bank debit-Bacs}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay/Payments - Create => Scenario15-Bank debit-Bacs/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario16-Bank Redirect-Trustly}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay/Payments - Retrieve => Scenario16-Bank Redirect-Trustly/Payments - Confirm}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly => Scenario16-Bank Redirect-Trustly}/Payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly => Scenario16-Bank Redirect-Trustly}/Payments - Confirm/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay/Payments - Retrieve => Scenario16-Bank Redirect-Trustly/Payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach/Payments - Confirm => Scenario16-Bank Redirect-Trustly/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario16-Bank Redirect-Trustly}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly => Scenario16-Bank Redirect-Trustly}/Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach/Payments - Confirm => Scenario16-Bank Redirect-Trustly/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach/Payments - Create => Scenario16-Bank Redirect-Trustly/Payments - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay => Scenario16-Bank Redirect-Trustly}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario15-Bank Redirect-giropay => Scenario16-Bank Redirect-Trustly}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach/Payments - Create => Scenario16-Bank Redirect-Trustly/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/List payment methods for a Customer/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/List payment methods for a Customer/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/List payment methods for a Customer/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach/Payments - Retrieve => Scenario17-Add card flow/List payment methods for a Customer}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario17-Add card flow}/Payments - Create/request.json (83%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs/Payments - Confirm => Scenario17-Add card flow/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Payments - Retrieve Copy/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Payments - Retrieve Copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Payments - Retrieve Copy/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs/Payments - Create => Scenario17-Add card flow/Payments - Retrieve Copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Refunds - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Refunds - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Refunds - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs/Payments - Retrieve => Scenario17-Add card flow/Refunds - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Refunds - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Refunds - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Refunds - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Confirm => Scenario17-Add card flow/Refunds - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Save card payments - Confirm/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Save card payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario17-Add card flow}/Save card payments - Confirm/request.json (67%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Create => Scenario17-Add card flow/Save card payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Save card payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Save card payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario17-Add card flow}/Save card payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Retrieve => Scenario17-Add card flow/Save card payments - Create}/response.json (100%) delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/.meta.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/event.test.js delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/event.test.js rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/List payment methods for a Customer/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/List payment methods for a Customer/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/List payment methods for a Customer/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/List payment methods for a Customer/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Create/event.prerequest.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Create/request.json (83%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Create/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow/Payments - Retrieve Copy => Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Save card payments - Confirm/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Save card payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Save card payments - Confirm/request.json (65%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow/Refunds - Create => Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Save card payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Save card payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment => Scenario18-Pass Invalid CVV for save card flow and verify failed payment}/Save card payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow/Refunds - Retrieve => Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/List payment methods for a Customer/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/List payment methods for a Customer/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/List payment methods for a Customer/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow/Save card payments - Confirm => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Create/event.prerequest.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Create/request.json (83%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow/Save card payments - Create => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Save card payments - Confirm/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Save card payments - Confirm/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario19-Add card flow => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Save card payments - Confirm/request.json (68%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Save card payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Save card payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy}/Save card payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve => Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario20-Create Gift Card payment}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach/Payments - Retrieve => Scenario20-Create Gift Card payment/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario20-Create Gift Card payment}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario20-Create Gift Card payment}/Payments - Create/request.json (77%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm => Scenario20-Create Gift Card payment/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs/Payments - Confirm => Scenario20-Create Gift Card payment/Payments - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario20-Create Gift Card payment}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario16-Bank debit-ach => Scenario20-Create Gift Card payment}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create => Scenario20-Create Gift Card payment/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario8-Refund full payment}/Payments - Create/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario11-Partial refund => Scenario8-Refund full payment}/Payments - Create/request.json (78%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer => Scenario8-Refund full payment/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario8-Refund full payment}/Payments - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario17-Bank debit-Bacs => Scenario8-Refund full payment}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create => Scenario8-Refund full payment/Payments - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Confirm => Scenario8-Refund full payment/Refunds - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/Refunds - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/Refunds - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve => Scenario8-Refund full payment/Refunds - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Create => Scenario8-Refund full payment/Refunds - Retrieve}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/Refunds - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment => Scenario8-Refund full payment}/Refunds - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm => Scenario8-Refund full payment/Refunds - Retrieve}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario9-Create a mandate and recurring payment}/.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Retrieve => Scenario9-Create a mandate and recurring payment/Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario9-Create a mandate and recurring payment}/Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario9-Create a mandate and recurring payment}/Payments - Create/request.json (83%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create => Scenario9-Create a mandate and recurring payment/Payments - Create}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment/Payments - Create => Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario9-Create a mandate and recurring payment}/Payments - Retrieve-copy/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario18-Bank Redirect-Trustly/Payments - Retrieve => Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy}/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment/Payments - Create => Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy}/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario9-Create a mandate and recurring payment}/Payments - Retrieve/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario14-Refund recurring payment => Scenario9-Create a mandate and recurring payment}/Payments - Retrieve/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario9-Create a mandate and recurring payment}/Payments - Retrieve/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario22-Create Gift Card payment => Scenario9-Create a mandate and recurring payment}/Payments - Retrieve/response.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment/Payments - Create => Scenario9-Create a mandate and recurring payment/Recurring Payments - Create}/.event.meta.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario9-Create a mandate and recurring payment}/Recurring Payments - Create/event.test.js (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario10-Create a mandate and recurring payment => Scenario9-Create a mandate and recurring payment}/Recurring Payments - Create/request.json (100%) rename postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/{Scenario9-Refund full payment/Payments - Create => Scenario9-Create a mandate and recurring payment/Recurring Payments - Create}/response.json (100%) delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/.event.meta.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/request.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/response.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/.event.meta.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/response.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/.event.meta.json delete mode 100644 postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/.meta.json index 6e38554431e..8bfdb580671 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/.meta.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/.meta.json @@ -7,18 +7,18 @@ "Scenario5-Void the payment", "Scenario6-Create 3DS payment", "Scenario7-Create 3DS payment with confirm false", - "Scenario9-Refund full payment", - "Scenario10-Create a mandate and recurring payment", - "Scenario11-Partial refund", - "Scenario12-Bank Redirect-sofort", - "Scenario13-Bank Redirect-eps", - "Scenario14-Refund recurring payment", - "Scenario15-Bank Redirect-giropay", - "Scenario16-Bank debit-ach", - "Scenario17-Bank debit-Bacs", - "Scenario18-Bank Redirect-Trustly", - "Scenario19-Add card flow", - "Scenario20-Pass Invalid CVV for save card flow and verify failed payment", - "Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy" + "Scenario8-Refund full payment", + "Scenario9-Create a mandate and recurring payment", + "Scenario10-Partial refund", + "Scenario11-Bank Redirect-sofort", + "Scenario12-Bank Redirect-eps", + "Scenario13-Refund recurring payment", + "Scenario14-Bank debit-ach", + "Scenario15-Bank debit-Bacs", + "Scenario16-Bank Redirect-Trustly", + "Scenario17-Add card flow", + "Scenario18-Pass Invalid CVV for save card flow and verify failed payment", + "Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy", + "Scenario20-Create Gift Card payment" ] } \ No newline at end of file diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json index ad2b0902947..e6fe8306704 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -73,6 +73,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/request.json similarity index 80% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/request.json index ad2b0902947..e6fe8306704 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -73,6 +73,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve-copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create-copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve-copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve-copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Partial refund/Refunds - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create-copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve-copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Bank Redirect-sofort/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Refunds - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-eps/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/request.json similarity index 82% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/request.json index dc5f34024bb..1719e207ade 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "setup_future_usage": "off_session", @@ -78,7 +78,7 @@ "zip": "94122", "country": "US", "first_name": "Joseph", - "last_name":"Doe" + "last_name": "Doe" } }, "billing": { @@ -91,7 +91,7 @@ "zip": "94122", "country": "US", "first_name": "Joseph", - "last_name":"Doe" + "last_name": "Doe" } }, "statement_descriptor_name": "joseph", @@ -100,6 +100,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve-copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario12-Bank Redirect-sofort/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve-copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Bank Redirect-eps/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Recurring Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Create Copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario13-Refund recurring payment/Refunds - Retrieve Copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Recurring Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Create Copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Bank debit-ach/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/event.test.js deleted file mode 100644 index 924d98f8f9d..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/event.test.js +++ /dev/null @@ -1,103 +0,0 @@ -// Validate status 2xx -pm.test("[POST]::/payments/:id/confirm - Status code is 2xx", function () { - pm.response.to.be.success; -}); - -// Validate if response header has matching content-type -pm.test( - "[POST]::/payments/:id/confirm - Content-Type is application/json", - function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", - ); - }, -); - -// Validate if response has JSON Body -pm.test("[POST]::/payments/:id/confirm - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); -}); - -// Set response object as internal variable -let jsonData = {}; -try { - jsonData = pm.response.json(); -} catch (e) {} - -// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id -if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log( - "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", - ); -} - -// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id -if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log( - "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", - ); -} - -// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret -if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log( - "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, - ); -} else { - console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", - ); -} - -// Response body should have value "requires_customer_action" for "status" -if (jsonData?.status) { - pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'requires_customer_action'", - function () { - pm.expect(jsonData.status).to.eql("requires_customer_action"); - }, - ); -} - -// Response body should have "next_action.redirect_to_url" -pm.test( - "[POST]::/payments - Content check if 'next_action.redirect_to_url' exists", - function () { - pm.expect(typeof jsonData.next_action.redirect_to_url !== "undefined").to.be - .true; - }, -); - -// Response body should have value "giropay" for "payment_method_type" -if (jsonData?.payment_method_type) { - pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'payment_method_type' matches 'giropay'", - function () { - pm.expect(jsonData.payment_method_type).to.eql("giropay"); - }, - ); -} - -// Response body should have value "stripe" for "connector" -if (jsonData?.connector) { - pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'connector' matches 'adyen'", - function () { - pm.expect(jsonData.connector).to.eql("adyen"); - }, - ); -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/request.json deleted file mode 100644 index 384d0bfc486..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/request.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "value", - "value": "{{publishable_key}}", - "type": "string" - }, - { - "key": "key", - "value": "api-key", - "type": "string" - }, - { - "key": "in", - "value": "header", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw_json_formatted": { - "client_secret": "{{client_secret}}", - "payment_method": "bank_redirect", - "payment_method_type": "giropay", - "payment_method_data": { - "bank_redirect": { - "giropay": { - "billing_details": { - "billing_name": "John Doe" - }, - "bank_name": "ing", - "preferred_language": "en", - "country": "DE" - } - } - }, - "browser_info": { - "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", - "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", - "language": "nl-NL", - "color_depth": 24, - "screen_height": 723, - "screen_width": 1536, - "time_zone": 0, - "java_enabled": true, - "java_script_enabled": true, - "ip_address": "127.0.0.1" - } - } - }, - "url": { - "raw": "{{baseUrl}}/payments/:id/confirm", - "host": ["{{baseUrl}}"], - "path": ["payments", ":id", "confirm"], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/request.json deleted file mode 100644 index 8f091a4238b..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/request.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw_json_formatted": { - "amount": 1000, - "currency": "EUR", - "confirm": false, - "capture_method": "automatic", - "capture_on": "2022-09-10T10:11:12Z", - "amount_to_capture": 1000, - "customer_id": "StripeCustomer", - "email": "abcdef123@gmail.com", - "name": "John Doe", - "phone": "999999999", - "phone_country_code": "+65", - "description": "Its my first payment request", - "authentication_type": "three_ds", - "return_url": "https://duck.com", - "billing": { - "address": { - "first_name": "John", - "last_name": "Doe", - "line1": "1467", - "line2": "Harrison Street", - "line3": "Harrison Street", - "city": "San Fransico", - "state": "California", - "zip": "94122", - "country": "DE" - } - }, - "browser_info": { - "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", - "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", - "language": "nl-NL", - "color_depth": 24, - "screen_height": 723, - "screen_width": 1536, - "time_zone": 0, - "java_enabled": true, - "java_script_enabled": true, - "ip_address": "127.0.0.1" - }, - "shipping": { - "address": { - "line1": "1467", - "line2": "Harrison Street", - "line3": "Harrison Street", - "city": "San Fransico", - "state": "California", - "zip": "94122", - "country": "DE", - "first_name": "John", - "last_name": "Doe" - } - }, - "statement_descriptor_name": "joseph", - "statement_descriptor_suffix": "JS", - "metadata": { - "udf1": "value1", - "new_customer": "true", - "login_date": "2019-09-10T10:11:12Z" - } - } - }, - "url": { - "raw": "{{baseUrl}}/payments", - "host": ["{{baseUrl}}"], - "path": ["payments"] - }, - "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/request.json similarity index 80% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/request.json index 0d66f04b260..6640fa64176 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/request.json @@ -63,6 +63,17 @@ "reference": "daslvcgbaieh" } } + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Refunds - Retrieve Copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank debit-Bacs/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-giropay/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-Trustly/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/List payment methods for a Customer/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/request.json similarity index 83% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/request.json index 7b15b0ee871..aee22fa4add 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/request.json @@ -45,11 +45,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -92,6 +92,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Payments - Retrieve Copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Refunds - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/request.json similarity index 67% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/request.json index 68ba9ce708f..7db274d3426 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/request.json @@ -41,19 +41,24 @@ "client_secret": "{{client_secret}}", "payment_method": "card", "payment_token": "{{payment_token}}", - "card_cvc": "737" + "card_cvc": "737", + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id", - "confirm" - ], + "host": ["{{baseUrl}}"], + "path": ["payments", ":id", "confirm"], "variable": [ { "key": "id", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Add card flow/Save card payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/.meta.json deleted file mode 100644 index 57d3f8e2bc7..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/.meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "childrenOrder": [ - "Payments - Create", - "Payments - Confirm", - "Payments - Retrieve" - ] -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/event.test.js deleted file mode 100644 index 0444324000a..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/event.test.js +++ /dev/null @@ -1,71 +0,0 @@ -// Validate status 2xx -pm.test("[POST]::/payments - Status code is 2xx", function () { - pm.response.to.be.success; -}); - -// Validate if response header has matching content-type -pm.test("[POST]::/payments - Content-Type is application/json", function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", - ); -}); - -// Validate if response has JSON Body -pm.test("[POST]::/payments - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); -}); - -// Set response object as internal variable -let jsonData = {}; -try { - jsonData = pm.response.json(); -} catch (e) {} - -// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id -if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log( - "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", - ); -} - -// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id -if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log( - "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", - ); -} - -// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret -if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log( - "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, - ); -} else { - console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", - ); -} - -// Response body should have value "requires_payment_method" for "status" -if (jsonData?.status) { - pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'", - function () { - pm.expect(jsonData.status).to.eql("requires_payment_method"); - }, - ); -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/event.test.js deleted file mode 100644 index 9053ddab13b..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/event.test.js +++ /dev/null @@ -1,71 +0,0 @@ -// Validate status 2xx -pm.test("[GET]::/payments/:id - Status code is 2xx", function () { - pm.response.to.be.success; -}); - -// Validate if response header has matching content-type -pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", - ); -}); - -// Validate if response has JSON Body -pm.test("[GET]::/payments/:id - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); -}); - -// Set response object as internal variable -let jsonData = {}; -try { - jsonData = pm.response.json(); -} catch (e) {} - -// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id -if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log( - "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", - ); -} - -// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id -if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log( - "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, - ); -} else { - console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", - ); -} - -// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret -if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log( - "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, - ); -} else { - console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", - ); -} - -// Response body should have value "requires_customer_action" for "status" -if (jsonData?.status) { - pm.test( - "[POST]::/payments:id - Content check if value for 'status' matches 'requires_customer_action'", - function () { - pm.expect(jsonData.status).to.eql("requires_customer_action"); - }, - ); -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/List payment methods for a Customer/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.prerequest.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.prerequest.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.prerequest.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.prerequest.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json similarity index 83% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json index 7b15b0ee871..aee22fa4add 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json @@ -45,11 +45,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -92,6 +92,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Retrieve Copy/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/request.json similarity index 65% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/request.json index a1442beda71..10102b27394 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/request.json @@ -40,19 +40,25 @@ "raw_json_formatted": { "client_secret": "{{client_secret}}", "payment_method": "card", - "payment_token": "{{payment_token}}" + "payment_token": "{{payment_token}}", + "card_cvc": "123", + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id", - "confirm" - ], + "host": ["{{baseUrl}}"], + "path": ["payments", ":id", "confirm"], "variable": [ { "key": "id", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Refunds - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.prerequest.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.prerequest.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.prerequest.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.prerequest.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json similarity index 83% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json index 7b15b0ee871..aee22fa4add 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json @@ -45,11 +45,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -92,6 +92,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/List payment methods for a Customer/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/request.json similarity index 68% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/request.json index 5d78ead7de9..a2a47cc1156 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Save card payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/request.json @@ -41,19 +41,23 @@ "client_secret": "{{client_secret}}", "payment_method": "card", "payment_token": "{{payment_token}}", - "card_cvc": "7373" + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id", - "confirm" - ], + "host": ["{{baseUrl}}"], + "path": ["payments", ":id", "confirm"], "variable": [ { "key": "id", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json index ec45ef29bb6..4b1bd40ca34 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json @@ -38,7 +38,18 @@ } }, "raw_json_formatted": { - "client_secret": "{{client_secret}}" + "client_secret": "{{client_secret}}", + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json index 6a90dc1f517..101aa618814 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/request.json similarity index 77% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/request.json index f8e5000d314..e00c0bcf3d5 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/request.json @@ -35,12 +35,12 @@ "payment_method": "gift_card", "payment_method_type": "givex", "payment_method_data": { - "gift_card": { - "givex": { - "number": "6364530000000000", - "cvc": "122222" - } + "gift_card": { + "givex": { + "number": "6364530000000000", + "cvc": "122222" } + } }, "billing": { "address": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario16-Bank debit-ach/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Save card payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Create Gift Card payment/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json index 770b7326827..316f33ad0b9 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json @@ -43,12 +43,23 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json index 3f21848a011..e792688d1ba 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -73,6 +73,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json index 3f21848a011..e792688d1ba 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -73,6 +73,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/request.json similarity index 78% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/request.json index 08c88256395..e6fe8306704 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario11-Partial refund/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -73,17 +73,24 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, "url": { "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] + "host": ["{{baseUrl}}"], + "path": ["payments"] }, "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" } diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/List payment methods for a Customer/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario17-Bank debit-Bacs/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Confirm/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Confirm/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario8-Refund full payment/Refunds - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/request.json similarity index 83% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/request.json index 0bf892efdf5..7c7a9539be2 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "setup_future_usage": "off_session", @@ -98,6 +98,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Save card payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve-copy/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario18-Bank Redirect-Trustly/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve-copy/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario14-Refund recurring payment/Payments - Retrieve/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Retrieve/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Payments - Retrieve/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/.event.meta.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/.event.meta.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/.event.meta.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/event.test.js b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/event.test.js similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/event.test.js rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/event.test.js diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/request.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/request.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/request.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/response.json similarity index 100% rename from postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/response.json rename to postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Create a mandate and recurring payment/Recurring Payments - Create/response.json diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/.event.meta.json deleted file mode 100644 index 0731450e6b2..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/.event.meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "eventOrder": ["event.test.js"] -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/request.json deleted file mode 100644 index 6cd4b7d96c5..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/request.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/payments/:id?force_sync=true", - "host": ["{{baseUrl}}"], - "path": ["payments", ":id"], - "query": [ - { - "key": "force_sync", - "value": "true" - } - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/response.json deleted file mode 100644 index fe51488c706..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Retrieve/response.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/.event.meta.json deleted file mode 100644 index 0731450e6b2..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/.event.meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "eventOrder": ["event.test.js"] -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/response.json deleted file mode 100644 index fe51488c706..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Create/response.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/.event.meta.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/.event.meta.json deleted file mode 100644 index 0731450e6b2..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/.event.meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "eventOrder": ["event.test.js"] -} diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/response.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/response.json deleted file mode 100644 index fe51488c706..00000000000 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Refunds - Retrieve/response.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json index 8ac3ed14b0a..b259b51fafb 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json @@ -36,11 +36,11 @@ "payment_method_type": "credit", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -73,6 +73,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json index b994c6bfef1..4081d208e52 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json index 9f409826257..c3f3dae00ec 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json index c7ad4cb2749..47a19f70643 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json @@ -70,6 +70,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json index 1ae83751a40..063cd44d7a6 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json index 923cb4aae78..ea9ecd82436 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json @@ -35,12 +35,12 @@ "payment_method": "gift_card", "payment_method_type": "givex", "payment_method_data": { - "gift_card": { - "givex": { - "number": "6364530000000000", - "cvc": "122222" - } + "gift_card": { + "givex": { + "number": "6364530000000000", + "cvc": "122222" } + } }, "billing": { "address": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/ACH Payouts - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/ACH Payouts - Create/request.json index 451f18f195b..da4cd865e31 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/ACH Payouts - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/ACH Payouts - Create/request.json @@ -87,7 +87,18 @@ } }, "confirm": true, - "auto_fulfill": true + "auto_fulfill": true, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/Bacs Payouts - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/Bacs Payouts - Create/request.json index e639b9e3bc6..ea4da2248c6 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/Bacs Payouts - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create payouts using unsupported methods/Bacs Payouts - Create/request.json @@ -62,7 +62,18 @@ "auto_fulfill": true, "connector": ["adyen"], "business_label": "abcd", - "business_country": "US" + "business_country": "US", + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Confirm/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Confirm/request.json index ec45ef29bb6..4b1bd40ca34 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Confirm/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Confirm/request.json @@ -38,7 +38,18 @@ } }, "raw_json_formatted": { - "client_secret": "{{client_secret}}" + "client_secret": "{{client_secret}}", + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true + } } }, "url": { diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json index c029e72ab46..320bb4ed34c 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json @@ -35,11 +35,11 @@ "payment_method": "card", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json index e05de78af19..7a1f00b0c34 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json @@ -35,11 +35,11 @@ "payment_method": "card", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json index e05de78af19..7a1f00b0c34 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json @@ -35,11 +35,11 @@ "payment_method": "card", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json index e05de78af19..7a1f00b0c34 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json @@ -35,11 +35,11 @@ "payment_method": "card", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json index 19e3369a340..5ef8dd0c38d 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json @@ -35,11 +35,11 @@ "payment_method": "card", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "billing": { @@ -72,6 +72,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json index 40a78911a9a..ec2d9c7a7d7 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json @@ -35,11 +35,11 @@ "payment_method": "card", "payment_method_data": { "card": { - "card_number": "371449635398431", + "card_number": "4111111111111111", "card_exp_month": "03", "card_exp_year": "2030", "card_holder_name": "joseph Doe", - "card_cvc": "7373" + "card_cvc": "737" } }, "setup_future_usage": "off_session", @@ -97,6 +97,17 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" + }, + "browser_info": { + "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", + "accept_header": "text\\/html,application\\/xhtml+xml,application\\/xml;q=0.9,image\\/webp,image\\/apng,*\\/*;q=0.8", + "language": "en-GB", + "color_depth": 30, + "screen_height": 1117, + "screen_width": 1728, + "time_zone": -330, + "java_enabled": true, + "java_script_enabled": true } } }, diff --git a/postman/collection-json/adyen_uk.postman_collection.json b/postman/collection-json/adyen_uk.postman_collection.json index 522ff2084ac..512f84c6a4b 100644 --- a/postman/collection-json/adyen_uk.postman_collection.json +++ b/postman/collection-json/adyen_uk.postman_collection.json @@ -728,7 +728,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1207,248 +1207,6 @@ { "name": "Happy Cases", "item": [ - { - "name": "Scenario22-Create Gift Card payment", - "item": [ - { - "name": "Payments - Create", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw": "{\"amount\":1100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" - }, - "url": { - "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] - }, - "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" - }, - "response": [] - }, - { - "name": "Payments - Retrieve", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"Succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/payments/:id?force_sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id" - ], - "query": [ - { - "key": "force_sync", - "value": "true" - } - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" - }, - "response": [] - } - ] - }, { "name": "Scenario1-Create payment with confirm true", "item": [ @@ -1555,7 +1313,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1797,7 +1555,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1938,7 +1696,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\"}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -2330,7 +2088,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}}}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -2581,7 +2339,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2967,7 +2725,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3873,7 +3631,7 @@ ] }, { - "name": "Scenario9-Refund full payment", + "name": "Scenario8-Refund full payment", "item": [ { "name": "Payments - Create", @@ -3978,7 +3736,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -4304,7 +4062,7 @@ ] }, { - "name": "Scenario10-Create a mandate and recurring payment", + "name": "Scenario9-Create a mandate and recurring payment", "item": [ { "name": "Payments - Create", @@ -4435,7 +4193,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -4865,7 +4623,7 @@ ] }, { - "name": "Scenario11-Partial refund", + "name": "Scenario10-Partial refund", "item": [ { "name": "Payments - Create", @@ -4970,7 +4728,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -5609,7 +5367,7 @@ ] }, { - "name": "Scenario12-Bank Redirect-sofort", + "name": "Scenario11-Bank Redirect-sofort", "item": [ { "name": "Payments - Create", @@ -6030,7 +5788,7 @@ ] }, { - "name": "Scenario13-Bank Redirect-eps", + "name": "Scenario12-Bank Redirect-eps", "item": [ { "name": "Payments - Create", @@ -6451,7 +6209,7 @@ ] }, { - "name": "Scenario14-Refund recurring payment", + "name": "Scenario13-Refund recurring payment", "item": [ { "name": "Payments - Create", @@ -6582,7 +6340,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Joseph\",\"last_name\":\"Doe\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Joseph\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Joseph\",\"last_name\":\"Doe\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Joseph\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -7211,7 +6969,7 @@ ] }, { - "name": "Scenario15-Bank Redirect-giropay", + "name": "Scenario14-Bank debit-ach", "item": [ { "name": "Payments - Create", @@ -7316,7 +7074,7 @@ "language": "json" } }, - "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"DE\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"DE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":10000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -7403,433 +7161,12 @@ " );", "}", "", - "// Response body should have value \"requires_customer_action\" for \"status\"", - "if (jsonData?.status) {", + "// Response body should have value \"ach\" for \"payment_method_type\"", + "if (jsonData?.payment_method_type) {", " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'requires_customer_action'\",", + " \"[POST]::/payments/:id/confirm - Content check if value for 'payment_method_type' matches 'ach'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", - " },", - " );", - "}", - "", - "// Response body should have \"next_action.redirect_to_url\"", - "pm.test(", - " \"[POST]::/payments - Content check if 'next_action.redirect_to_url' exists\",", - " function () {", - " pm.expect(typeof jsonData.next_action.redirect_to_url !== \"undefined\").to.be", - " .true;", - " },", - ");", - "", - "// Response body should have value \"giropay\" for \"payment_method_type\"", - "if (jsonData?.payment_method_type) {", - " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'payment_method_type' matches 'giropay'\",", - " function () {", - " pm.expect(jsonData.payment_method_type).to.eql(\"giropay\");", - " },", - " );", - "}", - "", - "// Response body should have value \"stripe\" for \"connector\"", - "if (jsonData?.connector) {", - " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'connector' matches 'adyen'\",", - " function () {", - " pm.expect(jsonData.connector).to.eql(\"adyen\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "value", - "value": "{{publishable_key}}", - "type": "string" - }, - { - "key": "key", - "value": "api-key", - "type": "string" - }, - { - "key": "in", - "value": "header", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"giropay\",\"payment_method_data\":{\"bank_redirect\":{\"giropay\":{\"billing_details\":{\"billing_name\":\"John Doe\"},\"bank_name\":\"ing\",\"preferred_language\":\"en\",\"country\":\"DE\"}}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"}}" - }, - "url": { - "raw": "{{baseUrl}}/payments/:id/confirm", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id", - "confirm" - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" - }, - "response": [] - }, - { - "name": "Payments - Retrieve", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"requires_customer_action\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments:id - Content check if value for 'status' matches 'requires_customer_action'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/payments/:id?force_sync=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id" - ], - "query": [ - { - "key": "force_sync", - "value": "true" - } - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } - ] - }, - "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" - }, - "response": [] - } - ] - }, - { - "name": "Scenario16-Bank debit-ach", - "item": [ - { - "name": "Payments - Create", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"requires_payment_method\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_payment_method\");", - " },", - " );", - "}", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw": "{\"amount\":10000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" - }, - "url": { - "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] - }, - "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" - }, - "response": [] - }, - { - "name": "Payments - Confirm", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "// Validate status 2xx", - "pm.test(\"[POST]::/payments/:id/confirm - Status code is 2xx\", function () {", - " pm.response.to.be.success;", - "});", - "", - "// Validate if response header has matching content-type", - "pm.test(", - " \"[POST]::/payments/:id/confirm - Content-Type is application/json\",", - " function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - " },", - ");", - "", - "// Validate if response has JSON Body", - "pm.test(\"[POST]::/payments/:id/confirm - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", - "// Set response object as internal variable", - "let jsonData = {};", - "try {", - " jsonData = pm.response.json();", - "} catch (e) {}", - "", - "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", - "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(", - " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", - "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", - " );", - "}", - "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", - " );", - "} else {", - " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", - " );", - "}", - "", - "// Response body should have value \"ach\" for \"payment_method_type\"", - "if (jsonData?.payment_method_type) {", - " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'payment_method_type' matches 'ach'\",", - " function () {", - " pm.expect(jsonData.payment_method_type).to.eql(\"ach\");", + " pm.expect(jsonData.payment_method_type).to.eql(\"ach\");", " },", " );", "}", @@ -8044,7 +7381,7 @@ ] }, { - "name": "Scenario17-Bank debit-Bacs", + "name": "Scenario15-Bank debit-Bacs", "item": [ { "name": "Payments - Create", @@ -8310,7 +7647,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"bank_debit\",\"payment_method_type\":\"bacs\",\"payment_method_data\":{\"bank_debit\":{\"bacs_bank_debit\":{\"account_number\":\"40308669\",\"routing_number\":\"121000358\",\"sort_code\":\"560036\",\"shopper_email\":\"example@gmail.com\",\"card_holder_name\":\"joseph Doe\",\"bank_account_holder_name\":\"David Archer\",\"billing_details\":{\"houseNumberOrName\":\"50\",\"street\":\"Test Street\",\"city\":\"Amsterdam\",\"stateOrProvince\":\"NY\",\"postalCode\":\"12010\",\"country\":\"GB\",\"name\":\"A. Klaassen\",\"email\":\"abcd@gmail.com\"},\"reference\":\"daslvcgbaieh\"}}}}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"bank_debit\",\"payment_method_type\":\"bacs\",\"payment_method_data\":{\"bank_debit\":{\"bacs_bank_debit\":{\"account_number\":\"40308669\",\"routing_number\":\"121000358\",\"sort_code\":\"560036\",\"shopper_email\":\"example@gmail.com\",\"card_holder_name\":\"joseph Doe\",\"bank_account_holder_name\":\"David Archer\",\"billing_details\":{\"houseNumberOrName\":\"50\",\"street\":\"Test Street\",\"city\":\"Amsterdam\",\"stateOrProvince\":\"NY\",\"postalCode\":\"12010\",\"country\":\"GB\",\"name\":\"A. Klaassen\",\"email\":\"abcd@gmail.com\"},\"reference\":\"daslvcgbaieh\"}}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -8456,7 +7793,7 @@ ] }, { - "name": "Scenario18-Bank Redirect-Trustly", + "name": "Scenario16-Bank Redirect-Trustly", "item": [ { "name": "Payments - Create", @@ -8877,7 +8214,7 @@ ] }, { - "name": "Scenario19-Add card flow", + "name": "Scenario17-Add card flow", "item": [ { "name": "Payments - Create", @@ -8960,7 +8297,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9312,7 +8649,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_token\":\"{{payment_token}}\",\"card_cvc\":\"7373\"}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_token\":\"{{payment_token}}\",\"card_cvc\":\"737\",\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -9687,7 +9024,7 @@ ] }, { - "name": "Scenario20-Pass Invalid CVV for save card flow and verify failed payment", + "name": "Scenario18-Pass Invalid CVV for save card flow and verify failed payment", "item": [ { "name": "Payments - Create", @@ -9780,7 +9117,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10149,7 +9486,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_token\":\"{{payment_token}}\",\"card_cvc\":\"737\"}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_token\":\"{{payment_token}}\",\"card_cvc\":\"123\",\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -10330,7 +9667,7 @@ ] }, { - "name": "Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy", + "name": "Scenario19-Don't Pass CVV for save card flow and verify failed payment Copy", "item": [ { "name": "Payments - Create", @@ -10423,7 +9760,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10792,7 +10129,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_token\":\"{{payment_token}}\"}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_token\":\"{{payment_token}}\",\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -10971,6 +10308,248 @@ "response": [] } ] + }, + { + "name": "Scenario20-Create Gift Card payment", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":1100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"Succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] } ] }, @@ -11093,7 +10672,7 @@ "language": "json" } }, - "raw": "{\"amount\":14100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":14100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":14100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":14100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11340,7 +10919,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11473,7 +11052,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"01\",\"card_exp_year\":\"2023\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"01\",\"card_exp_year\":\"2023\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11606,7 +11185,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"10\",\"card_exp_year\":\"2022\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"10\",\"card_exp_year\":\"2022\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11739,7 +11318,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"12345\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"12345\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12011,7 +11590,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\"}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -12143,7 +11722,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12523,7 +12102,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12784,7 +12363,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -13430,7 +13009,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -13779,7 +13358,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -14154,7 +13733,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -14534,7 +14113,7 @@ "language": "json" } }, - "raw": "{\"amount\":10000,\"currency\":\"USD\",\"customer_id\":\"payout_customer\",\"email\":\"payout_customer@example.com\",\"name\":\"Doest John\",\"phone\":\"9123456789\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payout request\",\"connector\":[\"adyen\"],\"payout_type\":\"bank\",\"payout_method_data\":{\"bank\":{\"bank_routing_number\":\"110000000\",\"bank_account_number\":\"000123456789\",\"bank_name\":\"Stripe Test Bank\",\"bank_country_code\":\"US\",\"bank_city\":\"California\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"CA\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Doest\",\"last_name\":\"John\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"1\"}},\"entity_type\":\"Individual\",\"recurring\":false,\"metadata\":{\"ref\":\"123\",\"vendor_details\":{\"account_type\":\"custom\",\"business_type\":\"individual\",\"business_profile_mcc\":5045,\"business_profile_url\":\"https://www.pastebin.com\",\"business_profile_name\":\"pT\",\"company_address_line1\":\"address_full_match\",\"company_address_line2\":\"Kimberly Way\",\"company_address_postal_code\":\"31062\",\"company_address_city\":\"Milledgeville\",\"company_address_state\":\"GA\",\"company_phone\":\"+16168205366\",\"company_tax_id\":\"000000000\",\"company_owners_provided\":false,\"capabilities_card_payments\":true,\"capabilities_transfers\":true},\"individual_details\":{\"tos_acceptance_date\":1680581051,\"tos_acceptance_ip\":\"103.159.11.202\",\"individual_dob_day\":\"01\",\"individual_dob_month\":\"01\",\"individual_dob_year\":\"1901\",\"individual_id_number\":\"000000000\",\"individual_ssn_last_4\":\"0000\",\"external_account_account_holder_type\":\"individual\"}},\"confirm\":true,\"auto_fulfill\":true}" + "raw": "{\"amount\":10000,\"currency\":\"USD\",\"customer_id\":\"payout_customer\",\"email\":\"payout_customer@example.com\",\"name\":\"Doest John\",\"phone\":\"9123456789\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payout request\",\"connector\":[\"adyen\"],\"payout_type\":\"bank\",\"payout_method_data\":{\"bank\":{\"bank_routing_number\":\"110000000\",\"bank_account_number\":\"000123456789\",\"bank_name\":\"Stripe Test Bank\",\"bank_country_code\":\"US\",\"bank_city\":\"California\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"CA\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Doest\",\"last_name\":\"John\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"1\"}},\"entity_type\":\"Individual\",\"recurring\":false,\"metadata\":{\"ref\":\"123\",\"vendor_details\":{\"account_type\":\"custom\",\"business_type\":\"individual\",\"business_profile_mcc\":5045,\"business_profile_url\":\"https://www.pastebin.com\",\"business_profile_name\":\"pT\",\"company_address_line1\":\"address_full_match\",\"company_address_line2\":\"Kimberly Way\",\"company_address_postal_code\":\"31062\",\"company_address_city\":\"Milledgeville\",\"company_address_state\":\"GA\",\"company_phone\":\"+16168205366\",\"company_tax_id\":\"000000000\",\"company_owners_provided\":false,\"capabilities_card_payments\":true,\"capabilities_transfers\":true},\"individual_details\":{\"tos_acceptance_date\":1680581051,\"tos_acceptance_ip\":\"103.159.11.202\",\"individual_dob_day\":\"01\",\"individual_dob_month\":\"01\",\"individual_dob_year\":\"1901\",\"individual_id_number\":\"000000000\",\"individual_ssn_last_4\":\"0000\",\"external_account_account_holder_type\":\"individual\"}},\"confirm\":true,\"auto_fulfill\":true,\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payouts/create", @@ -14639,7 +14218,7 @@ "language": "json" } }, - "raw": "{\"amount\":1,\"currency\":\"GBP\",\"customer_id\":\"payout_customer\",\"email\":\"payout_customer@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payout request\",\"payout_type\":\"bank\",\"payout_method_data\":{\"bank\":{\"bank_sort_code\":\"231470\",\"bank_account_number\":\"28821822\",\"bank_name\":\"Deutsche Bank\",\"bank_country_code\":\"NL\",\"bank_city\":\"Amsterdam\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"CA\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"entity_type\":\"Individual\",\"recurring\":true,\"metadata\":{\"ref\":\"123\"},\"confirm\":true,\"auto_fulfill\":true,\"connector\":[\"adyen\"],\"business_label\":\"abcd\",\"business_country\":\"US\"}" + "raw": "{\"amount\":1,\"currency\":\"GBP\",\"customer_id\":\"payout_customer\",\"email\":\"payout_customer@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payout request\",\"payout_type\":\"bank\",\"payout_method_data\":{\"bank\":{\"bank_sort_code\":\"231470\",\"bank_account_number\":\"28821822\",\"bank_name\":\"Deutsche Bank\",\"bank_country_code\":\"NL\",\"bank_city\":\"Amsterdam\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"CA\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"entity_type\":\"Individual\",\"recurring\":true,\"metadata\":{\"ref\":\"123\"},\"confirm\":true,\"auto_fulfill\":true,\"connector\":[\"adyen\"],\"business_label\":\"abcd\",\"business_country\":\"US\",\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36\",\"accept_header\":\"text\\\\/html,application\\\\/xhtml+xml,application\\\\/xml;q=0.9,image\\\\/webp,image\\\\/apng,*\\\\/*;q=0.8\",\"language\":\"en-GB\",\"color_depth\":30,\"screen_height\":1117,\"screen_width\":1728,\"time_zone\":-330,\"java_enabled\":true,\"java_script_enabled\":true}}" }, "url": { "raw": "{{baseUrl}}/payouts/create", From 99f593389451c13e81ba6d019e9f3ca4361942ed Mon Sep 17 00:00:00 2001 From: Amisha Prabhat <55580080+Aprabhat19@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:52:19 +0530 Subject: [PATCH 15/48] feat(routing): build gRPC Client Interface to initiate communication with other gRPC services (#5835) Co-authored-by: prajjwalkumar17 Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Nishant Joshi --- .github/workflows/CI-pr.yml | 9 + .github/workflows/CI-push.yml | 9 + .../workflows/postman-collection-runner.yml | 4 + Cargo.lock | 238 +++++++++++++++- Dockerfile | 2 +- crates/api_models/src/routing.rs | 11 +- crates/external_services/Cargo.toml | 13 +- crates/external_services/build.rs | 15 + crates/external_services/src/grpc_client.rs | 48 ++++ .../src/grpc_client/dynamic_routing.rs | 265 ++++++++++++++++++ crates/external_services/src/lib.rs | 3 + crates/hyperswitch_interfaces/src/api.rs | 1 - crates/router/Cargo.toml | 1 + .../src/configs/secrets_transformers.rs | 1 + crates/router/src/configs/settings.rs | 2 + crates/router/src/core/payments.rs | 3 +- crates/router/src/routes/admin.rs | 4 +- crates/router/src/routes/api_keys.rs | 4 +- crates/router/src/routes/app.rs | 8 +- crates/router/src/routes/dummy_connector.rs | 8 +- crates/router/src/routes/routing.rs | 8 +- docker-compose-development.yml | 4 +- .../up.sql | 6 +- proto/success_rate.proto | 57 ++++ 24 files changed, 691 insertions(+), 33 deletions(-) create mode 100644 crates/external_services/build.rs create mode 100644 crates/external_services/src/grpc_client.rs create mode 100644 crates/external_services/src/grpc_client/dynamic_routing.rs create mode 100644 proto/success_rate.proto diff --git a/.github/workflows/CI-pr.yml b/.github/workflows/CI-pr.yml index 4ea196e1c22..c5b005849ae 100644 --- a/.github/workflows/CI-pr.yml +++ b/.github/workflows/CI-pr.yml @@ -121,6 +121,9 @@ jobs: with: toolchain: "${{ env.rust_version }}" + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install sccache uses: taiki-e/install-action@v2.33.28 with: @@ -219,6 +222,9 @@ jobs: toolchain: stable 2 weeks ago components: clippy + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install sccache uses: taiki-e/install-action@v2.33.28 with: @@ -298,6 +304,9 @@ jobs: with: toolchain: stable 2 weeks ago + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install rust cache uses: Swatinem/rust-cache@v2.7.0 diff --git a/.github/workflows/CI-push.yml b/.github/workflows/CI-push.yml index 0f92fc36c31..dc7195714bd 100644 --- a/.github/workflows/CI-push.yml +++ b/.github/workflows/CI-push.yml @@ -71,6 +71,9 @@ jobs: with: toolchain: "${{ env.rust_version }}" + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - uses: Swatinem/rust-cache@v2.7.0 with: save-if: ${{ github.event_name == 'push' }} @@ -150,6 +153,9 @@ jobs: toolchain: stable 2 weeks ago components: clippy + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install cargo-hack uses: baptiste0928/cargo-install@v2.2.0 with: @@ -217,6 +223,9 @@ jobs: with: toolchain: stable 2 weeks ago + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install rust cache uses: Swatinem/rust-cache@v2.7.0 diff --git a/.github/workflows/postman-collection-runner.yml b/.github/workflows/postman-collection-runner.yml index 38776759639..95949593fbf 100644 --- a/.github/workflows/postman-collection-runner.yml +++ b/.github/workflows/postman-collection-runner.yml @@ -94,6 +94,10 @@ jobs: if: ${{ ((github.event_name == 'pull_request') && (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name)) || (github.event_name == 'merge_group')}} uses: Swatinem/rust-cache@v2.7.0 + - name: Install Protoc + if: ${{ ((github.event_name == 'pull_request') && (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name)) || (github.event_name == 'merge_group')}} + uses: arduino/setup-protoc@v3 + - name: Install Diesel CLI with Postgres Support if: ${{ ((github.event_name == 'pull_request') && (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name)) || (github.event_name == 'merge_group')}} uses: baptiste0928/cargo-install@v2.2.0 diff --git a/Cargo.lock b/Cargo.lock index 11036b42dc4..d90c577152d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1433,7 +1433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes 1.7.1", "futures-util", @@ -1454,6 +1454,33 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "bytes 1.7.1", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.1", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.3.4" @@ -1471,6 +1498,26 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes 1.7.1", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.73" @@ -3064,6 +3111,7 @@ dependencies = [ name = "external_services" version = "0.1.0" dependencies = [ + "api_models", "async-trait", "aws-config 0.55.3", "aws-sdk-kms", @@ -3081,10 +3129,15 @@ dependencies = [ "hyperswitch_interfaces", "masking", "once_cell", + "prost 0.13.2", "router_env", "serde", "thiserror", "tokio 1.40.0", + "tonic 0.12.2", + "tonic-build", + "tonic-reflection", + "tonic-types", "vaultrs", ] @@ -3160,6 +3213,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.33" @@ -3854,6 +3913,19 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper 1.4.1", + "hyper-util", + "pin-project-lite", + "tokio 1.40.0", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -4727,6 +4799,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + [[package]] name = "mutually_exclusive_features" version = "0.0.3" @@ -5114,10 +5192,10 @@ dependencies = [ "http 0.2.12", "opentelemetry", "opentelemetry-proto", - "prost", + "prost 0.11.9", "thiserror", "tokio 1.40.0", - "tonic", + "tonic 0.8.3", ] [[package]] @@ -5129,8 +5207,8 @@ dependencies = [ "futures 0.3.30", "futures-util", "opentelemetry", - "prost", - "tonic", + "prost 0.11.9", + "tonic 0.8.3", ] [[package]] @@ -5389,6 +5467,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.5.0", +] + [[package]] name = "phf" version = "0.11.2" @@ -5591,6 +5679,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.77", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -5679,7 +5777,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes 1.7.1", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +dependencies = [ + "bytes 1.7.1", + "prost-derive 0.13.2", +] + +[[package]] +name = "prost-build" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" +dependencies = [ + "bytes 1.7.1", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.13.2", + "prost-types", + "regex", + "syn 2.0.77", + "tempfile", ] [[package]] @@ -5695,6 +5824,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "prost-types" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +dependencies = [ + "prost 0.13.2", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -8215,7 +8366,7 @@ checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.6.20", "base64 0.13.1", "bytes 1.7.1", "futures-core", @@ -8224,11 +8375,11 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.30", - "hyper-timeout", + "hyper-timeout 0.4.1", "percent-encoding", "pin-project", - "prost", - "prost-derive", + "prost 0.11.9", + "prost-derive 0.11.9", "tokio 1.40.0", "tokio-stream", "tokio-util", @@ -8239,6 +8390,73 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "tonic" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.7.5", + "base64 0.22.1", + "bytes 1.7.1", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-timeout 0.5.1", + "hyper-util", + "percent-encoding", + "pin-project", + "prost 0.13.2", + "socket2", + "tokio 1.40.0", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tonic-reflection" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b56b874eedb04f89907573b408eab1e87c1c1dce43aac6ad63742f57faa99ff" +dependencies = [ + "prost 0.13.2", + "prost-types", + "tokio 1.40.0", + "tokio-stream", + "tonic 0.12.2", +] + +[[package]] +name = "tonic-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d967793411bc1a5392accf4731114295f0fd122865d22cde46a8584b03402b2" +dependencies = [ + "prost 0.13.2", + "prost-types", + "tonic 0.12.2", +] + [[package]] name = "totp-rs" version = "5.6.0" diff --git a/Dockerfile b/Dockerfile index 29c1003c115..65ca6370364 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG EXTRA_FEATURES="" ARG VERSION_FEATURE_SET="v1" RUN apt-get update \ - && apt-get install -y libpq-dev libssl-dev pkg-config + && apt-get install -y libpq-dev libssl-dev pkg-config protobuf-compiler # Copying codebase from current dir to /router dir # and creating a fresh build diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 5023d30f722..2b551eaebb7 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -197,7 +197,9 @@ pub enum RoutableChoiceSerde { impl std::fmt::Display for RoutableConnectorChoice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let base = self.connector.to_string(); - + if let Some(mca_id) = &self.merchant_connector_id { + return write!(f, "{}:{}", base, mca_id.get_string_repr()); + } write!(f, "{}", base) } } @@ -252,6 +254,11 @@ impl From for RoutableChoiceSerde { } } +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct RoutableConnectorChoiceWithStatus { + pub routable_connector_choice: RoutableConnectorChoice, + pub status: bool, +} #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, strum::Display, ToSchema)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] @@ -565,7 +572,7 @@ impl Default for SuccessBasedRoutingConfig { } } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema, strum::Display)] pub enum SuccessBasedRoutingConfigParams { PaymentMethod, PaymentMethodType, diff --git a/crates/external_services/Cargo.toml b/crates/external_services/Cargo.toml index 9051c58ea59..a882cdc9f51 100644 --- a/crates/external_services/Cargo.toml +++ b/crates/external_services/Cargo.toml @@ -13,6 +13,7 @@ email = ["dep:aws-config"] aws_s3 = ["dep:aws-config", "dep:aws-sdk-s3"] hashicorp-vault = ["dep:vaultrs"] v1 = ["hyperswitch_interfaces/v1"] +dynamic_routing = ["dep:prost", "dep:tonic", "dep:tonic-reflection", "dep:tonic-types", "dep:api_models", "tokio/macros", "tokio/rt-multi-thread"] [dependencies] async-trait = "0.1.79" @@ -31,14 +32,24 @@ hyper-proxy = "0.9.1" once_cell = "1.19.0" serde = { version = "1.0.197", features = ["derive"] } thiserror = "1.0.58" -tokio = "1.37.0" vaultrs = { version = "0.7.2", optional = true } +prost = { version = "0.13", optional = true } +tokio = "1.37.0" +tonic = { version = "0.12.2", optional = true } +tonic-reflection = { version = "0.12.2", optional = true } +tonic-types = { version = "0.12.2", optional = true } + # First party crates common_utils = { version = "0.1.0", path = "../common_utils" } hyperswitch_interfaces = { version = "0.1.0", path = "../hyperswitch_interfaces", default-features = false } masking = { version = "0.1.0", path = "../masking" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } +api_models = { version = "0.1.0", path = "../api_models", optional = true } + + +[build-dependencies] +tonic-build = "0.12" [lints] workspace = true diff --git a/crates/external_services/build.rs b/crates/external_services/build.rs new file mode 100644 index 00000000000..61e19f308d8 --- /dev/null +++ b/crates/external_services/build.rs @@ -0,0 +1,15 @@ +use std::{env, path::PathBuf}; + +#[allow(clippy::expect_used)] +fn main() -> Result<(), Box> { + // Get the directory of the current crate + let crate_dir = env::var("CARGO_MANIFEST_DIR")?; + let proto_file = PathBuf::from(crate_dir) + .join("..") + .join("..") + .join("proto") + .join("success_rate.proto"); + // Compile the .proto file + tonic_build::compile_protos(proto_file).expect("Failed to compile protos "); + Ok(()) +} diff --git a/crates/external_services/src/grpc_client.rs b/crates/external_services/src/grpc_client.rs new file mode 100644 index 00000000000..5afd3024551 --- /dev/null +++ b/crates/external_services/src/grpc_client.rs @@ -0,0 +1,48 @@ +/// Dyanimc Routing Client interface implementation +#[cfg(feature = "dynamic_routing")] +pub mod dynamic_routing; +use std::{fmt::Debug, sync::Arc}; + +#[cfg(feature = "dynamic_routing")] +use dynamic_routing::{DynamicRoutingClientConfig, RoutingStrategy}; +use router_env::logger; +use serde; + +/// Struct contains all the gRPC Clients +#[derive(Debug, Clone)] +pub struct GrpcClients { + /// The routing client + #[cfg(feature = "dynamic_routing")] + pub dynamic_routing: RoutingStrategy, +} +/// Type that contains the configs required to construct a gRPC client with its respective services. +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, Default)] +pub struct GrpcClientSettings { + #[cfg(feature = "dynamic_routing")] + /// Configs for Dynamic Routing Client + pub dynamic_routing_client: DynamicRoutingClientConfig, +} + +impl GrpcClientSettings { + /// # Panics + /// + /// This function will panic if it fails to establish a connection with the gRPC server. + /// This function will be called at service startup. + #[allow(clippy::expect_used)] + pub async fn get_grpc_client_interface(&self) -> Arc { + #[cfg(feature = "dynamic_routing")] + let dynamic_routing_connection = self + .dynamic_routing_client + .clone() + .get_dynamic_routing_connection() + .await + .expect("Failed to establish a connection with the Dynamic Routing Server"); + + logger::info!("Connection established with gRPC Server"); + + Arc::new(GrpcClients { + #[cfg(feature = "dynamic_routing")] + dynamic_routing: dynamic_routing_connection, + }) + } +} diff --git a/crates/external_services/src/grpc_client/dynamic_routing.rs b/crates/external_services/src/grpc_client/dynamic_routing.rs new file mode 100644 index 00000000000..17bd43ca3a8 --- /dev/null +++ b/crates/external_services/src/grpc_client/dynamic_routing.rs @@ -0,0 +1,265 @@ +use std::fmt::Debug; + +use api_models::routing::{ + CurrentBlockThreshold, RoutableConnectorChoice, RoutableConnectorChoiceWithStatus, + SuccessBasedRoutingConfig, SuccessBasedRoutingConfigBody, +}; +use common_utils::{errors::CustomResult, ext_traits::OptionExt, transformers::ForeignTryFrom}; +use error_stack::ResultExt; +use serde; +use success_rate::{ + success_rate_calculator_client::SuccessRateCalculatorClient, CalSuccessRateConfig, + CalSuccessRateRequest, CalSuccessRateResponse, + CurrentBlockThreshold as DynamicCurrentThreshold, LabelWithStatus, + UpdateSuccessRateWindowConfig, UpdateSuccessRateWindowRequest, UpdateSuccessRateWindowResponse, +}; +use tonic::transport::Channel; +#[allow( + missing_docs, + unused_qualifications, + clippy::unwrap_used, + clippy::as_conversions +)] +pub mod success_rate { + tonic::include_proto!("success_rate"); +} +/// Result type for Dynamic Routing +pub type DynamicRoutingResult = CustomResult; + +/// Dynamic Routing Errors +#[derive(Debug, Clone, thiserror::Error)] +pub enum DynamicRoutingError { + /// The required input is missing + #[error("Missing Required Field : {field} for building the Dynamic Routing Request")] + MissingRequiredField { + /// The required field name + field: String, + }, + /// Error from Dynamic Routing Server + #[error("Error from Dynamic Routing Server : {0}")] + SuccessRateBasedRoutingFailure(String), +} + +/// Type that consists of all the services provided by the client +#[derive(Debug, Clone)] +pub struct RoutingStrategy { + /// success rate service for Dynamic Routing + pub success_rate_client: Option>, +} + +/// Contains the Dynamic Routing Client Config +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, Default)] +#[serde(untagged)] +pub enum DynamicRoutingClientConfig { + /// If the dynamic routing client config has been enabled + Enabled { + /// The host for the client + host: String, + /// The port of the client + port: u16, + }, + #[default] + /// If the dynamic routing client config has been disabled + Disabled, +} + +impl DynamicRoutingClientConfig { + /// establish connection with the server + pub async fn get_dynamic_routing_connection( + self, + ) -> Result> { + let success_rate_client = match self { + Self::Enabled { host, port } => { + let uri = format!("http://{}:{}", host, port); + let channel = tonic::transport::Endpoint::new(uri)?.connect().await?; + Some(SuccessRateCalculatorClient::new(channel)) + } + Self::Disabled => None, + }; + Ok(RoutingStrategy { + success_rate_client, + }) + } +} + +/// The trait Success Based Dynamic Routing would have the functions required to support the calculation and updation window +#[async_trait::async_trait] +pub trait SuccessBasedDynamicRouting: dyn_clone::DynClone + Send + Sync { + /// To calculate the success rate for the list of chosen connectors + async fn calculate_success_rate( + &self, + id: String, + success_rate_based_config: SuccessBasedRoutingConfig, + label_input: Vec, + ) -> DynamicRoutingResult; + /// To update the success rate with the given label + async fn update_success_rate( + &self, + id: String, + success_rate_based_config: SuccessBasedRoutingConfig, + response: Vec, + ) -> DynamicRoutingResult; +} + +#[async_trait::async_trait] +impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { + async fn calculate_success_rate( + &self, + id: String, + success_rate_based_config: SuccessBasedRoutingConfig, + label_input: Vec, + ) -> DynamicRoutingResult { + let params = success_rate_based_config + .params + .map(|vec| { + vec.into_iter().fold(String::new(), |mut acc_vec, params| { + if !acc_vec.is_empty() { + acc_vec.push(':') + } + acc_vec.push_str(params.to_string().as_str()); + acc_vec + }) + }) + .get_required_value("params") + .change_context(DynamicRoutingError::MissingRequiredField { + field: "params".to_string(), + })?; + + let labels = label_input + .into_iter() + .map(|conn_choice| conn_choice.to_string()) + .collect::>(); + + let config = success_rate_based_config + .config + .map(ForeignTryFrom::foreign_try_from) + .transpose()?; + + let request = tonic::Request::new(CalSuccessRateRequest { + id, + params, + labels, + config, + }); + + let mut client = self.clone(); + + let response = client + .fetch_success_rate(request) + .await + .change_context(DynamicRoutingError::SuccessRateBasedRoutingFailure( + "Failed to fetch the success rate".to_string(), + ))? + .into_inner(); + + Ok(response) + } + + async fn update_success_rate( + &self, + id: String, + success_rate_based_config: SuccessBasedRoutingConfig, + label_input: Vec, + ) -> DynamicRoutingResult { + let config = success_rate_based_config + .config + .map(ForeignTryFrom::foreign_try_from) + .transpose()?; + + let labels_with_status = label_input + .into_iter() + .map(|conn_choice| LabelWithStatus { + label: conn_choice.routable_connector_choice.to_string(), + status: conn_choice.status, + }) + .collect(); + + let params = success_rate_based_config + .params + .map(|vec| { + vec.into_iter().fold(String::new(), |mut acc_vec, params| { + if !acc_vec.is_empty() { + acc_vec.push(':') + } + acc_vec.push_str(params.to_string().as_str()); + acc_vec + }) + }) + .get_required_value("params") + .change_context(DynamicRoutingError::MissingRequiredField { + field: "params".to_string(), + })?; + + let request = tonic::Request::new(UpdateSuccessRateWindowRequest { + id, + params, + labels_with_status, + config, + }); + + let mut client = self.clone(); + + let response = client + .update_success_rate_window(request) + .await + .change_context(DynamicRoutingError::SuccessRateBasedRoutingFailure( + "Failed to update the success rate window".to_string(), + ))? + .into_inner(); + + Ok(response) + } +} + +impl ForeignTryFrom for DynamicCurrentThreshold { + type Error = error_stack::Report; + fn foreign_try_from(current_threshold: CurrentBlockThreshold) -> Result { + Ok(Self { + duration_in_mins: current_threshold.duration_in_mins, + max_total_count: current_threshold + .max_total_count + .get_required_value("max_total_count") + .change_context(DynamicRoutingError::MissingRequiredField { + field: "max_total_count".to_string(), + })?, + }) + } +} + +impl ForeignTryFrom for UpdateSuccessRateWindowConfig { + type Error = error_stack::Report; + fn foreign_try_from(config: SuccessBasedRoutingConfigBody) -> Result { + Ok(Self { + max_aggregates_size: config + .max_aggregates_size + .get_required_value("max_aggregate_size") + .change_context(DynamicRoutingError::MissingRequiredField { + field: "max_aggregates_size".to_string(), + })?, + current_block_threshold: config + .current_block_threshold + .map(ForeignTryFrom::foreign_try_from) + .transpose()?, + }) + } +} + +impl ForeignTryFrom for CalSuccessRateConfig { + type Error = error_stack::Report; + fn foreign_try_from(config: SuccessBasedRoutingConfigBody) -> Result { + Ok(Self { + min_aggregates_size: config + .min_aggregates_size + .get_required_value("min_aggregate_size") + .change_context(DynamicRoutingError::MissingRequiredField { + field: "min_aggregates_size".to_string(), + })?, + default_success_rate: config + .default_success_rate + .get_required_value("default_success_rate") + .change_context(DynamicRoutingError::MissingRequiredField { + field: "default_success_rate".to_string(), + })?, + }) + } +} diff --git a/crates/external_services/src/lib.rs b/crates/external_services/src/lib.rs index f4bcd91e343..4570a5e5960 100644 --- a/crates/external_services/src/lib.rs +++ b/crates/external_services/src/lib.rs @@ -14,6 +14,9 @@ pub mod hashicorp_vault; pub mod no_encryption; +/// Building grpc clients to communicate with the server +pub mod grpc_client; + pub mod managers; /// Crate specific constants diff --git a/crates/hyperswitch_interfaces/src/api.rs b/crates/hyperswitch_interfaces/src/api.rs index 0b4545a33d7..7205865f24a 100644 --- a/crates/hyperswitch_interfaces/src/api.rs +++ b/crates/hyperswitch_interfaces/src/api.rs @@ -16,7 +16,6 @@ pub mod payouts; pub mod payouts_v2; pub mod refunds; pub mod refunds_v2; - use common_enums::enums::{CallConnectorAction, CaptureMethod, PaymentAction, PaymentMethodType}; use common_utils::{ errors::CustomResult, diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index bae6243d2e6..442561c2ca3 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -38,6 +38,7 @@ v1 = ["common_default", "api_models/v1", "diesel_models/v1", "hyperswitch_domain customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"] payment_v2 = ["api_models/payment_v2", "diesel_models/payment_v2", "hyperswitch_domain_models/payment_v2", "storage_impl/payment_v2"] payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"] +dynamic_routing = ["external_services/dynamic_routing"] # Partial Auth # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request. diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 9b0bfbb40b2..604dce5047c 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -497,6 +497,7 @@ pub(crate) async fn fetch_raw_secrets( user_auth_methods, decision: conf.decision, locker_based_open_banking_connectors: conf.locker_based_open_banking_connectors, + grpc_client: conf.grpc_client, recipient_emails: conf.recipient_emails, network_tokenization_supported_card_networks: conf .network_tokenization_supported_card_networks, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 78ff709cc02..7d6dfd26eae 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -13,6 +13,7 @@ use error_stack::ResultExt; use external_services::email::EmailSettings; use external_services::{ file_storage::FileStorageConfig, + grpc_client::GrpcClientSettings, managers::{ encryption_management::EncryptionManagementConfig, secrets_management::SecretsManagementConfig, @@ -120,6 +121,7 @@ pub struct Settings { pub user_auth_methods: SecretStateContainer, pub decision: Option, pub locker_based_open_banking_connectors: LockerBasedRecipientConnectorList, + pub grpc_client: GrpcClientSettings, pub recipient_emails: RecipientMails, pub network_tokenization_supported_card_networks: NetworkTokenizationSupportedCardNetworks, pub network_tokenization_service: Option>, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 0af5bf6037a..30d619efedc 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -11,7 +11,6 @@ pub mod routing; pub mod tokenization; pub mod transformers; pub mod types; - #[cfg(feature = "olap")] use std::collections::HashMap; use std::{ @@ -160,7 +159,6 @@ where &header_payload, ) .await?; - utils::validate_profile_id_from_auth_layer( profile_id_from_auth_layer, &payment_data.get_payment_intent().clone(), @@ -4427,6 +4425,7 @@ where ) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; + let connectors = routing::perform_eligibility_analysis_with_fallback( &state.clone(), key_store, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index a68f648e178..8ba214f94b9 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -361,7 +361,7 @@ pub async fn connector_retrieve( }) .into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -384,7 +384,7 @@ pub async fn connector_retrieve( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Merchant Connector - Retrieve diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index 71632cc5749..a00e740b3e6 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -166,7 +166,7 @@ pub async fn api_key_update( payload.key_id = key_id; payload.merchant_id.clone_from(&merchant_id); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -182,7 +182,7 @@ pub async fn api_key_update( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index f2be2f7bf50..75b32976e1d 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -9,7 +9,7 @@ use common_enums::TransactionType; use common_utils::crypto::Blake3; #[cfg(feature = "email")] use external_services::email::{ses::AwsSes, EmailService}; -use external_services::file_storage::FileStorageInterface; +use external_services::{file_storage::FileStorageInterface, grpc_client::GrpcClients}; use hyperswitch_interfaces::{ encryption_interface::EncryptionManagementInterface, secrets_interface::secret_state::{RawSecret, SecuredSecret}, @@ -105,6 +105,7 @@ pub struct SessionState { pub tenant: Tenant, #[cfg(feature = "olap")] pub opensearch_client: Arc, + pub grpc_client: Arc, } impl scheduler::SchedulerSessionState for SessionState { fn get_db(&self) -> Box { @@ -202,6 +203,7 @@ pub struct AppState { pub request_id: Option, pub file_storage_client: Arc, pub encryption_client: Arc, + pub grpc_client: Arc, } impl scheduler::SchedulerAppState for AppState { fn get_tenants(&self) -> Vec { @@ -351,6 +353,8 @@ impl AppState { let file_storage_client = conf.file_storage.get_file_storage_client().await; + let grpc_client = conf.grpc_client.get_grpc_client_interface().await; + Self { flow_name: String::from("default"), stores, @@ -367,6 +371,7 @@ impl AppState { request_id: None, file_storage_client, encryption_client, + grpc_client, } }) .await @@ -447,6 +452,7 @@ impl AppState { email_client: Arc::clone(&self.email_client), #[cfg(feature = "olap")] opensearch_client: Arc::clone(&self.opensearch_client), + grpc_client: Arc::clone(&self.grpc_client), }) } } diff --git a/crates/router/src/routes/dummy_connector.rs b/crates/router/src/routes/dummy_connector.rs index 26c32776d72..e376bcd7f9e 100644 --- a/crates/router/src/routes/dummy_connector.rs +++ b/crates/router/src/routes/dummy_connector.rs @@ -46,7 +46,7 @@ pub async fn dummy_connector_complete_payment( attempt_id, confirm: json_payload.confirm, }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -54,7 +54,7 @@ pub async fn dummy_connector_complete_payment( |state, _: (), req, _| core::payment_complete(state, req), &auth::NoAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentCreate))] @@ -65,7 +65,7 @@ pub async fn dummy_connector_payment( ) -> impl actix_web::Responder { let payload = json_payload.into_inner(); let flow = types::Flow::DummyPaymentCreate; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -73,7 +73,7 @@ pub async fn dummy_connector_payment( |state, _: (), req, _| core::payment(state, req), &auth::NoAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentRetrieve))] diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 86ad2078f6c..70da80c6756 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -612,7 +612,7 @@ pub async fn retrieve_surcharge_decision_manager_config( req: HttpRequest, ) -> impl Responder { let flow = Flow::DecisionManagerRetrieveConfig; - oss_api::server_wrap( + Box::pin(oss_api::server_wrap( flow, state, &req, @@ -638,7 +638,7 @@ pub async fn retrieve_surcharge_decision_manager_config( minimum_entity_level: EntityType::Merchant, }, api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -727,7 +727,7 @@ pub async fn retrieve_decision_manager_config( req: HttpRequest, ) -> impl Responder { let flow = Flow::DecisionManagerRetrieveConfig; - oss_api::server_wrap( + Box::pin(oss_api::server_wrap( flow, state, &req, @@ -750,7 +750,7 @@ pub async fn retrieve_decision_manager_config( minimum_entity_level: EntityType::Merchant, }, api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/docker-compose-development.yml b/docker-compose-development.yml index ebccb114048..cf12982a9bd 100644 --- a/docker-compose-development.yml +++ b/docker-compose-development.yml @@ -60,7 +60,9 @@ services: ### Application services hyperswitch-server: image: rust:latest - command: cargo run --bin router -- -f ./config/docker_compose.toml + command: | + apt-get install -y protobuf-compiler && \ + cargo run --bin router -- -f ./config/docker_compose.toml working_dir: /app ports: - "8080:8080" diff --git a/migrations/2024-09-05-160455_add_new_col_is_dynamic_routing_algorithm_in_business_profile/up.sql b/migrations/2024-09-05-160455_add_new_col_is_dynamic_routing_algorithm_in_business_profile/up.sql index 2482fc9f591..c7a53898129 100644 --- a/migrations/2024-09-05-160455_add_new_col_is_dynamic_routing_algorithm_in_business_profile/up.sql +++ b/migrations/2024-09-05-160455_add_new_col_is_dynamic_routing_algorithm_in_business_profile/up.sql @@ -1,3 +1,5 @@ -- Your SQL goes here -ALTER TABLE business_profile -ADD COLUMN dynamic_routing_algorithm JSON DEFAULT NULL; +ALTER TABLE + business_profile +ADD + COLUMN dynamic_routing_algorithm JSON DEFAULT NULL; \ No newline at end of file diff --git a/proto/success_rate.proto b/proto/success_rate.proto new file mode 100644 index 00000000000..8018f6d5fe4 --- /dev/null +++ b/proto/success_rate.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package success_rate; + + service SuccessRateCalculator { + rpc FetchSuccessRate (CalSuccessRateRequest) returns (CalSuccessRateResponse); + + rpc UpdateSuccessRateWindow (UpdateSuccessRateWindowRequest) returns (UpdateSuccessRateWindowResponse); + } + + // API-1 types + message CalSuccessRateRequest { + string id = 1; + string params = 2; + repeated string labels = 3; + CalSuccessRateConfig config = 4; + } + + message CalSuccessRateConfig { + uint32 min_aggregates_size = 1; + double default_success_rate = 2; + } + + message CalSuccessRateResponse { + repeated LabelWithScore labels_with_score = 1; + } + + message LabelWithScore { + double score = 1; + string label = 2; + } + + // API-2 types + message UpdateSuccessRateWindowRequest { + string id = 1; + string params = 2; + repeated LabelWithStatus labels_with_status = 3; + UpdateSuccessRateWindowConfig config = 4; + } + + message LabelWithStatus { + string label = 1; + bool status = 2; + } + + message UpdateSuccessRateWindowConfig { + uint32 max_aggregates_size = 1; + CurrentBlockThreshold current_block_threshold = 2; + } + + message CurrentBlockThreshold { + optional uint64 duration_in_mins = 1; + uint64 max_total_count = 2; + } + + message UpdateSuccessRateWindowResponse { + string message = 1; + } \ No newline at end of file From ed13146b8088e1fcd6df8b820fa8c7b4a9e400a3 Mon Sep 17 00:00:00 2001 From: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:58:39 +0530 Subject: [PATCH 16/48] docs: api-reference changes for customers and admin list apis for v2 (#5936) --- .../merchant-connector--list.mdx | 3 +++ .../customers/customers--create.mdx | 3 +++ .../customers/customers--delete.mdx | 3 +++ .../api-reference/customers/customers--list.mdx | 3 +++ .../customers/customers--retrieve.mdx | 3 +++ .../customers/customers--update.mdx | 3 +++ .../merchant-account/business-profile--list.mdx | 3 +++ .../merchant-account--create.mdx | 2 +- .../merchant-account--retrieve.mdx | 2 +- .../merchant-account--update.mdx | 2 +- .../organization/merchant-account--list.mdx | 3 +++ api-reference-v2/mint.json | 17 +++++++++++++++-- api-reference-v2/openapi_spec.json | 6 +++--- .../api-reference/customers/customers--list.mdx | 2 +- api-reference/openapi_spec.json | 2 +- crates/openapi/src/routes/customers.rs | 6 ++++-- 16 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 api-reference-v2/api-reference/business-profile/merchant-connector--list.mdx create mode 100644 api-reference-v2/api-reference/customers/customers--create.mdx create mode 100644 api-reference-v2/api-reference/customers/customers--delete.mdx create mode 100644 api-reference-v2/api-reference/customers/customers--list.mdx create mode 100644 api-reference-v2/api-reference/customers/customers--retrieve.mdx create mode 100644 api-reference-v2/api-reference/customers/customers--update.mdx create mode 100644 api-reference-v2/api-reference/merchant-account/business-profile--list.mdx create mode 100644 api-reference-v2/api-reference/organization/merchant-account--list.mdx diff --git a/api-reference-v2/api-reference/business-profile/merchant-connector--list.mdx b/api-reference-v2/api-reference/business-profile/merchant-connector--list.mdx new file mode 100644 index 00000000000..6560f45e5fa --- /dev/null +++ b/api-reference-v2/api-reference/business-profile/merchant-connector--list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/profiles/{profile_id}/connector_accounts +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/customers/customers--create.mdx b/api-reference-v2/api-reference/customers/customers--create.mdx new file mode 100644 index 00000000000..c53a4cd6209 --- /dev/null +++ b/api-reference-v2/api-reference/customers/customers--create.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v2/customers +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/customers/customers--delete.mdx b/api-reference-v2/api-reference/customers/customers--delete.mdx new file mode 100644 index 00000000000..eee5cffd597 --- /dev/null +++ b/api-reference-v2/api-reference/customers/customers--delete.mdx @@ -0,0 +1,3 @@ +--- +openapi: delete /v2/customers/{id} +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/customers/customers--list.mdx b/api-reference-v2/api-reference/customers/customers--list.mdx new file mode 100644 index 00000000000..432370e119a --- /dev/null +++ b/api-reference-v2/api-reference/customers/customers--list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/customers/list +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/customers/customers--retrieve.mdx b/api-reference-v2/api-reference/customers/customers--retrieve.mdx new file mode 100644 index 00000000000..e89fe53d42a --- /dev/null +++ b/api-reference-v2/api-reference/customers/customers--retrieve.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/customers/{id} +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/customers/customers--update.mdx b/api-reference-v2/api-reference/customers/customers--update.mdx new file mode 100644 index 00000000000..65433d8fad4 --- /dev/null +++ b/api-reference-v2/api-reference/customers/customers--update.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v2/customers/{id} +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-account/business-profile--list.mdx b/api-reference-v2/api-reference/merchant-account/business-profile--list.mdx new file mode 100644 index 00000000000..e14bc0d6ef3 --- /dev/null +++ b/api-reference-v2/api-reference/merchant-account/business-profile--list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/merchant_accounts/{account_id}/profiles +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-account/merchant-account--create.mdx b/api-reference-v2/api-reference/merchant-account/merchant-account--create.mdx index c49307151ba..d870b811aae 100644 --- a/api-reference-v2/api-reference/merchant-account/merchant-account--create.mdx +++ b/api-reference-v2/api-reference/merchant-account/merchant-account--create.mdx @@ -1,3 +1,3 @@ --- -openapi: post /v2/accounts +openapi: post /v2/merchant_accounts --- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-account/merchant-account--retrieve.mdx b/api-reference-v2/api-reference/merchant-account/merchant-account--retrieve.mdx index 277c69892c0..d082565234e 100644 --- a/api-reference-v2/api-reference/merchant-account/merchant-account--retrieve.mdx +++ b/api-reference-v2/api-reference/merchant-account/merchant-account--retrieve.mdx @@ -1,3 +1,3 @@ --- -openapi: get /v2/accounts/{id} +openapi: get /v2/merchant_accounts/{id} --- \ No newline at end of file diff --git a/api-reference-v2/api-reference/merchant-account/merchant-account--update.mdx b/api-reference-v2/api-reference/merchant-account/merchant-account--update.mdx index 274d7fad3d7..51f80ceea30 100644 --- a/api-reference-v2/api-reference/merchant-account/merchant-account--update.mdx +++ b/api-reference-v2/api-reference/merchant-account/merchant-account--update.mdx @@ -1,3 +1,3 @@ --- -openapi: put /v2/accounts/{id} +openapi: put /v2/merchant_accounts/{id} --- \ No newline at end of file diff --git a/api-reference-v2/api-reference/organization/merchant-account--list.mdx b/api-reference-v2/api-reference/organization/merchant-account--list.mdx new file mode 100644 index 00000000000..4057e7e18e5 --- /dev/null +++ b/api-reference-v2/api-reference/organization/merchant-account--list.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /v2/organization/{organization_id}/merchant_accounts +--- \ No newline at end of file diff --git a/api-reference-v2/mint.json b/api-reference-v2/mint.json index 975eb291507..ab7635a7cf2 100644 --- a/api-reference-v2/mint.json +++ b/api-reference-v2/mint.json @@ -39,7 +39,8 @@ "pages": [ "api-reference/organization/organization--create", "api-reference/organization/organization--retrieve", - "api-reference/organization/organization--update" + "api-reference/organization/organization--update", + "api-reference/organization/merchant-account--list" ] }, { @@ -47,7 +48,8 @@ "pages": [ "api-reference/merchant-account/merchant-account--create", "api-reference/merchant-account/merchant-account--retrieve", - "api-reference/merchant-account/merchant-account--update" + "api-reference/merchant-account/merchant-account--update", + "api-reference/merchant-account/business-profile--list" ] }, { @@ -56,6 +58,7 @@ "api-reference/business-profile/business-profile--create", "api-reference/business-profile/business-profile--update", "api-reference/business-profile/business-profile--retrieve", + "api-reference/business-profile/merchant-connector--list", "api-reference/business-profile/business-profile--activate-routing-algorithm", "api-reference/business-profile/business-profile--retrieve-active-routing-algorithm", "api-reference/business-profile/business-profile--deactivate-routing-algorithm", @@ -86,6 +89,16 @@ "api-reference/routing/routing--create", "api-reference/routing/routing--retrieve" ] + }, + { + "group": "Customers", + "pages": [ + "api-reference/customers/customers--create", + "api-reference/customers/customers--retrieve", + "api-reference/customers/customers--update", + "api-reference/customers/customers--delete", + "api-reference/customers/customers--list" + ] } ], "footerSocials": { diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 9db2fcfdfdc..04c72b1e8a7 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1438,8 +1438,8 @@ "tags": [ "Customers" ], - "summary": "Creates a customer object and stores the customer details to be reused for future payments.", - "description": "Incase the customer already exists in the system, this API will respond with the customer details.", + "summary": "Customers - Create", + "description": "Creates a customer object and stores the customer details to be reused for future payments.\nIncase the customer already exists in the system, this API will respond with the customer details.", "operationId": "Create a Customer", "requestBody": { "content": { @@ -1622,7 +1622,7 @@ } }, "/v2/customers/list": { - "post": { + "get": { "tags": [ "Customers" ], diff --git a/api-reference/api-reference/customers/customers--list.mdx b/api-reference/api-reference/customers/customers--list.mdx index 4130c644304..6fb6b632862 100644 --- a/api-reference/api-reference/customers/customers--list.mdx +++ b/api-reference/api-reference/customers/customers--list.mdx @@ -1,3 +1,3 @@ --- -openapi: openapi_spec post /customers/list +openapi: openapi_spec get /customers/list --- \ No newline at end of file diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index c542f3491a3..7ef33c1c945 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -2304,7 +2304,7 @@ } }, "/customers/list": { - "post": { + "get": { "tags": [ "Customers" ], diff --git a/crates/openapi/src/routes/customers.rs b/crates/openapi/src/routes/customers.rs index 26140252ebd..b4c61230752 100644 --- a/crates/openapi/src/routes/customers.rs +++ b/crates/openapi/src/routes/customers.rs @@ -93,7 +93,7 @@ pub async fn customers_delete() {} /// /// Lists all the customers for a particular merchant id. #[utoipa::path( - post, + get, path = "/customers/list", responses( (status = 200, description = "Customers retrieved", body = Vec), @@ -106,6 +106,8 @@ pub async fn customers_delete() {} #[cfg(feature = "v1")] pub async fn customers_list() {} +/// Customers - Create +/// /// Creates a customer object and stores the customer details to be reused for future payments. /// Incase the customer already exists in the system, this API will respond with the customer details. #[utoipa::path( @@ -199,7 +201,7 @@ pub async fn customers_delete() {} /// /// Lists all the customers for a particular merchant id. #[utoipa::path( - post, + get, path = "/v2/customers/list", responses( (status = 200, description = "Customers retrieved", body = Vec), From 1c827986c308a98291a8811e4314bc6bbea7f385 Mon Sep 17 00:00:00 2001 From: likhinbopanna <131246334+likhinbopanna@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:01:28 +0530 Subject: [PATCH 17/48] ci(postman): Update status for paypal postman collection (#5929) --- .../Payments - Create/event.test.js | 8 ++-- .../Payments - Retrieve/event.test.js | 8 ++-- .../Payments - Capture/event.test.js | 30 ++++++------- .../Payments - Retrieve/event.test.js | 28 ++++++------ .../Payments - Confirm/event.test.js | 8 ++-- .../Payments - Retrieve/event.test.js | 8 ++-- .../Payments - Confirm/event.test.js | 8 ++-- .../Payments - Retrieve/event.test.js | 8 ++-- .../Payments - Capture/event.test.js | 8 ++-- .../Payments - Retrieve/event.test.js | 28 ++++++------ .../Payments - Capture/event.test.js | 43 ++++++++----------- .../Payments - Retrieve/event.test.js | 28 ++++++------ .../Payments - Capture/event.test.js | 43 ++++++++++--------- .../Payments - Retrieve/event.test.js | 41 ++++++++++-------- .../Payments - Create/event.test.js | 8 ++-- .../Payments - Create/event.test.js | 22 +++++----- 16 files changed, 162 insertions(+), 165 deletions(-) diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js index aeb351e4dad..1a30711ba04 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js @@ -60,13 +60,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js index 78121d8fe5d..03d30ea4a15 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js @@ -60,13 +60,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments/:id - Content check if value for 'status' matches 'processing'", + "[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Capture/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Capture/event.test.js index 1fbef271836..5dae93d5723 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Capture/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Capture/event.test.js @@ -8,9 +8,9 @@ pm.test( "[POST]::/payments/:id/capture - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); - }, + } ); // Validate if response has JSON Body @@ -29,11 +29,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -42,11 +42,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -55,21 +55,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "partially_captured" for "status" if (jsonData?.status) { pm.test( - "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'", + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("partially_captured"); + } ); } @@ -84,7 +84,7 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } @@ -94,7 +94,7 @@ if (jsonData?.amount_received) { "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", function () { pm.expect(jsonData.amount_received).to.eql(6000); - }, + } ); } @@ -104,6 +104,6 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 6540", function () { pm.expect(jsonData.amount_capturable).to.eql(6540); - }, + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Retrieve/event.test.js index 06abc1d37a5..0bd9a4f1b23 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario11-Create Partial Capture payment/Payments - Retrieve/event.test.js @@ -6,7 +6,7 @@ pm.test("[GET]::/payments/:id - Status code is 2xx", function () { // Validate if response header has matching content-type pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); }); @@ -26,11 +26,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -39,11 +39,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -52,21 +52,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "partially_captured" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'partially_captured'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("partially_captured"); + } ); } @@ -81,7 +81,7 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } @@ -91,7 +91,7 @@ if (jsonData?.amount_received) { "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", function () { pm.expect(jsonData.amount_received).to.eql(6000); - }, + } ); } @@ -101,6 +101,6 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6540'", function () { pm.expect(jsonData.amount_capturable).to.eql(6540); - }, + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js index 615015e3f2f..1c6058fbade 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js @@ -63,13 +63,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'processing'", + "[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js index 286e0db8439..15e0f819b48 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js @@ -60,13 +60,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments:id - Content check if value for 'status' matches 'processing'", + "[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js index caeefcaa565..05808ceade7 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js @@ -63,13 +63,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments:id/confirm - Content check if value for 'status' matches 'processing'", + "[POST]::/payments:id/confirm - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js index 286e0db8439..15e0f819b48 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js @@ -60,13 +60,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments:id - Content check if value for 'status' matches 'processing'", + "[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Capture/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Capture/event.test.js index 6b58fbb86f0..17b89550995 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Capture/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Capture/event.test.js @@ -63,13 +63,13 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "partially_captured" for "status" if (jsonData?.status) { pm.test( - "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'", + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("partially_captured"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Retrieve/event.test.js index 06abc1d37a5..0bd9a4f1b23 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture with confirm false/Payments - Retrieve/event.test.js @@ -6,7 +6,7 @@ pm.test("[GET]::/payments/:id - Status code is 2xx", function () { // Validate if response header has matching content-type pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); }); @@ -26,11 +26,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -39,11 +39,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -52,21 +52,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "partially_captured" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'partially_captured'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("partially_captured"); + } ); } @@ -81,7 +81,7 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } @@ -91,7 +91,7 @@ if (jsonData?.amount_received) { "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", function () { pm.expect(jsonData.amount_received).to.eql(6000); - }, + } ); } @@ -101,6 +101,6 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6540'", function () { pm.expect(jsonData.amount_capturable).to.eql(6540); - }, + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js index 572f74e6d57..8d98f8b9c88 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js @@ -8,9 +8,9 @@ pm.test( "[POST]::/payments/:id/capture - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); - }, + } ); // Validate if response has JSON Body @@ -29,11 +29,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -42,11 +42,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -55,21 +55,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'", + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } @@ -84,17 +84,17 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } -// Response body should have value "6000" for "amount_received" +// Response body should have value "6540" for "amount_received" if (jsonData?.amount_received) { pm.test( - "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", + "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6540'", function () { pm.expect(jsonData.amount_received).to.eql(6540); - }, + } ); } @@ -104,16 +104,7 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 6540", function () { pm.expect(jsonData.amount_capturable).to.eql(6540); - }, + } ); } -// Response body should have value "0" for "amount_received" -if (jsonData?.amount_received) { - pm.test( - "[post]:://payments/:id/capture - Content check if value for 'amount_received' matches 0", - function () { - pm.expect(jsonData.amount_received).to.eql(0); - }, - ); -} diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js index 021d1f1475c..3a6ef4049bd 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js @@ -6,7 +6,7 @@ pm.test("[GET]::/payments/:id - Status code is 2xx", function () { // Validate if response header has matching content-type pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); }); @@ -26,11 +26,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -39,11 +39,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -52,21 +52,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } @@ -81,7 +81,7 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } @@ -91,7 +91,7 @@ if (jsonData?.amount_received) { "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", function () { pm.expect(jsonData.amount_received).to.eql(6540); - }, + } ); } @@ -101,6 +101,6 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6540'", function () { pm.expect(jsonData.amount_capturable).to.eql(6540); - }, + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Capture/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Capture/event.test.js index b09744cff12..738f50438be 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Capture/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Capture/event.test.js @@ -8,9 +8,9 @@ pm.test( "[POST]::/payments/:id/capture - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); - }, + } ); // Validate if response has JSON Body @@ -29,11 +29,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -42,11 +42,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -55,21 +55,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "partially_captured" for "status" if (jsonData?.status) { pm.test( - "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'", + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("partially_captured"); + } ); } @@ -84,7 +84,7 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } @@ -94,7 +94,7 @@ if (jsonData?.amount_received) { "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", function () { pm.expect(jsonData.amount_received).to.eql(6000); - }, + } ); } @@ -104,13 +104,16 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 6550", function () { pm.expect(jsonData.amount_capturable).to.eql(6550); - }, + } ); } // Response body should have a valid surcharge_details field" -pm.test("Check if valid 'surcharge_details' is present in the response", function () { - pm.response.to.have.jsonBody('surcharge_details'); - pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5); - pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5); -}); +pm.test( + "Check if valid 'surcharge_details' is present in the response", + function () { + pm.response.to.have.jsonBody("surcharge_details"); + pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5); + pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5); + } +); diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Retrieve/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Retrieve/event.test.js index 69fd5563460..86e0844b6df 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Retrieve/event.test.js @@ -6,7 +6,7 @@ pm.test("[GET]::/payments/:id - Status code is 2xx", function () { // Validate if response header has matching content-type pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); }); @@ -26,11 +26,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -39,11 +39,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -52,21 +52,21 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "partially_captured" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'partially_captured'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("partially_captured"); + } ); } @@ -81,7 +81,7 @@ if (jsonData?.amount) { "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", function () { pm.expect(jsonData.amount).to.eql(6540); - }, + } ); } @@ -91,7 +91,7 @@ if (jsonData?.amount_received) { "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", function () { pm.expect(jsonData.amount_received).to.eql(6000); - }, + } ); } @@ -101,13 +101,16 @@ if (jsonData?.amount_capturable) { "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6550'", function () { pm.expect(jsonData.amount_capturable).to.eql(6550); - }, + } ); } // Response body should have a valid surcharge_details field" -pm.test("Check if valid 'surcharge_details' is present in the response", function () { - pm.response.to.have.jsonBody('surcharge_details'); - pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5); - pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5); -}); +pm.test( + "Check if valid 'surcharge_details' is present in the response", + function () { + pm.response.to.have.jsonBody("surcharge_details"); + pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5); + pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5); + } +); diff --git a/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/event.test.js index 16c37817f0b..d8aaaa5b2ed 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/event.test.js @@ -60,12 +60,12 @@ if (jsonData?.client_secret) { ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/event.test.js index 16c37817f0b..e019fcd3c25 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/event.test.js @@ -6,7 +6,7 @@ pm.test("[POST]::/payments - Status code is 2xx", function () { // Validate if response header has matching content-type pm.test("[POST]::/payments - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); }); @@ -26,11 +26,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -39,11 +39,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -52,20 +52,20 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } -// Response body should have value "processing" for "status" +// Response body should have value "succeeded" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'processing'", + "[POST]::/payments - Content check if value for 'status' matches 'succeeded'", function () { - pm.expect(jsonData.status).to.eql("processing"); - }, + pm.expect(jsonData.status).to.eql("succeeded"); + } ); } From 8c99db72adbd2b5f03c37fa5b1fa82b9c77ce2c5 Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:56:35 +0530 Subject: [PATCH 18/48] fix(merchant_account_v2): remove compatible_connector field in metadata (#5935) --- crates/api_models/src/admin.rs | 11 +---------- crates/router/src/core/admin.rs | 8 +------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 1846ef0e45a..3b9a8e1c983 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -188,7 +188,7 @@ pub struct MerchantAccountCreate { /// Metadata is useful for storing additional, unstructured information about the merchant account. #[schema(value_type = Option, example = r#"{ "city": "NY", "unit": "245" }"#)] - pub metadata: Option, + pub metadata: Option, /// The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] @@ -210,15 +210,6 @@ impl MerchantAccountCreate { .transpose() } - pub fn get_metadata_as_secret( - &self, - ) -> CustomResult, errors::ParsingError> { - self.metadata - .as_ref() - .map(|metadata| metadata.encode_to_value().map(Secret::new)) - .transpose() - } - pub fn get_primary_details_as_value( &self, ) -> CustomResult { diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 690d16596b8..95b02f0cdb3 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -606,12 +606,6 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { let publishable_key = create_merchant_publishable_key(); let db = &*state.store; - let metadata = self.get_metadata_as_secret().change_context( - errors::ApiErrorResponse::InvalidDataValue { - field_name: "metadata", - }, - )?; - let merchant_details = self.get_merchant_details_as_secret().change_context( errors::ApiErrorResponse::InvalidDataValue { field_name: "merchant_details", @@ -659,7 +653,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { }) .await?, publishable_key, - metadata, + metadata: self.metadata, storage_scheme: MerchantStorageScheme::PostgresOnly, created_at: date_time::now(), modified_at: date_time::now(), From 407c70cb151582ec3b2b661363811390dfeca8b2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:19:58 +0000 Subject: [PATCH 19/48] chore(postman): update Postman collection files --- .../paypal.postman_collection.json | 327 +++++++++--------- 1 file changed, 162 insertions(+), 165 deletions(-) diff --git a/postman/collection-json/paypal.postman_collection.json b/postman/collection-json/paypal.postman_collection.json index e6eab06d801..b4309b2b4a0 100644 --- a/postman/collection-json/paypal.postman_collection.json +++ b/postman/collection-json/paypal.postman_collection.json @@ -1027,9 +1027,9 @@ " \"[POST]::/payments/:id/capture - Content-Type is application/json\",", " function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", - " },", + " }", ");", "", "// Validate if response has JSON Body", @@ -1048,11 +1048,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -1061,11 +1061,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -1074,21 +1074,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"partially_captured\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'\",", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " }", " );", "}", "", @@ -1103,7 +1103,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -1113,7 +1113,7 @@ " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6000);", - " },", + " }", " );", "}", "", @@ -1123,16 +1123,19 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 6550\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6550);", - " },", + " }", " );", "}", "", "// Response body should have a valid surcharge_details field\"", - "pm.test(\"Check if valid 'surcharge_details' is present in the response\", function () {", - " pm.response.to.have.jsonBody('surcharge_details');", - " pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5);", - " pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5);", - "});", + "pm.test(", + " \"Check if valid 'surcharge_details' is present in the response\",", + " function () {", + " pm.response.to.have.jsonBody(\"surcharge_details\");", + " pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5);", + " pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5);", + " }", + ");", "" ], "type": "text/javascript" @@ -1197,7 +1200,7 @@ "// Validate if response header has matching content-type", "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", "});", "", @@ -1217,11 +1220,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -1230,11 +1233,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -1243,21 +1246,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"partially_captured\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'partially_captured'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " }", " );", "}", "", @@ -1272,7 +1275,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -1282,7 +1285,7 @@ " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6000);", - " },", + " }", " );", "}", "", @@ -1292,16 +1295,19 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6550'\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6550);", - " },", + " }", " );", "}", "", "// Response body should have a valid surcharge_details field\"", - "pm.test(\"Check if valid 'surcharge_details' is present in the response\", function () {", - " pm.response.to.have.jsonBody('surcharge_details');", - " pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5);", - " pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5);", - "});", + "pm.test(", + " \"Check if valid 'surcharge_details' is present in the response\",", + " function () {", + " pm.response.to.have.jsonBody(\"surcharge_details\");", + " pm.expect(jsonData.surcharge_details.surcharge_amount).to.eql(5);", + " pm.expect(jsonData.surcharge_details.tax_amount).to.eql(5);", + " }", + ");", "" ], "type": "text/javascript" @@ -1417,13 +1423,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -1566,13 +1572,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments/:id - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -1861,13 +1867,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -2033,13 +2039,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments:id - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -2333,13 +2339,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments:id/confirm - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments:id/confirm - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -2505,13 +2511,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments:id - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -2989,13 +2995,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"partially_captured\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'\",", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " }", " );", "}", "", @@ -3097,7 +3103,7 @@ "// Validate if response header has matching content-type", "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", "});", "", @@ -3117,11 +3123,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -3130,11 +3136,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -3143,21 +3149,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"partially_captured\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'partially_captured'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " }", " );", "}", "", @@ -3172,7 +3178,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -3182,7 +3188,7 @@ " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6000);", - " },", + " }", " );", "}", "", @@ -3192,7 +3198,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6540'\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6540);", - " },", + " }", " );", "}", "" @@ -3411,9 +3417,9 @@ " \"[POST]::/payments/:id/capture - Content-Type is application/json\",", " function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", - " },", + " }", ");", "", "// Validate if response has JSON Body", @@ -3432,11 +3438,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -3445,11 +3451,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -3458,21 +3464,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'\",", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -3487,17 +3493,17 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", - "// Response body should have value \"6000\" for \"amount_received\"", + "// Response body should have value \"6540\" for \"amount_received\"", "if (jsonData?.amount_received) {", " pm.test(", - " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", + " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6540'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -3507,19 +3513,10 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 6540\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6540);", - " },", + " }", " );", "}", "", - "// Response body should have value \"0\" for \"amount_received\"", - "if (jsonData?.amount_received) {", - " pm.test(", - " \"[post]:://payments/:id/capture - Content check if value for 'amount_received' matches 0\",", - " function () {", - " pm.expect(jsonData.amount_received).to.eql(0);", - " },", - " );", - "}", "" ], "type": "text/javascript" @@ -3584,7 +3581,7 @@ "// Validate if response header has matching content-type", "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", "});", "", @@ -3604,11 +3601,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -3617,11 +3614,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -3630,21 +3627,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "", @@ -3659,7 +3656,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -3669,7 +3666,7 @@ " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -3679,7 +3676,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6540'\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6540);", - " },", + " }", " );", "}", "" @@ -3898,9 +3895,9 @@ " \"[POST]::/payments/:id/capture - Content-Type is application/json\",", " function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", - " },", + " }", ");", "", "// Validate if response has JSON Body", @@ -3919,11 +3916,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -3932,11 +3929,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -3945,21 +3942,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"partially_captured\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'\",", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " }", " );", "}", "", @@ -3974,7 +3971,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -3984,7 +3981,7 @@ " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6000);", - " },", + " }", " );", "}", "", @@ -3994,7 +3991,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 6540\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6540);", - " },", + " }", " );", "}", "" @@ -4061,7 +4058,7 @@ "// Validate if response header has matching content-type", "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", "});", "", @@ -4081,11 +4078,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -4094,11 +4091,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -4107,21 +4104,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"partially_captured\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'partially_captured'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " }", " );", "}", "", @@ -4136,7 +4133,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", " function () {", " pm.expect(jsonData.amount).to.eql(6540);", - " },", + " }", " );", "}", "", @@ -4146,7 +4143,7 @@ " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", " pm.expect(jsonData.amount_received).to.eql(6000);", - " },", + " }", " );", "}", "", @@ -4156,7 +4153,7 @@ " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 6540'\",", " function () {", " pm.expect(jsonData.amount_capturable).to.eql(6540);", - " },", + " }", " );", "}", "" @@ -6731,13 +6728,13 @@ " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "" @@ -6938,7 +6935,7 @@ "// Validate if response header has matching content-type", "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", "});", "", @@ -6958,11 +6955,11 @@ " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -6971,11 +6968,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -6984,21 +6981,21 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", "", - "// Response body should have value \"processing\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " }", " );", "}", "" From 769111c20b0f745a5e4fc3ed6c9a705f277c3d5b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:24:13 +0000 Subject: [PATCH 20/48] chore(version): 2024.09.19.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b0420e2e2d..27e966ee89b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.09.19.0 + +### Features + +- **disputes:** Add support for disputes aggregate ([#5896](https://github.com/juspay/hyperswitch/pull/5896)) ([`0a0c93e`](https://github.com/juspay/hyperswitch/commit/0a0c93e102dc3183627db3b871370d7b5661f151)) +- **payment_methods_v2:** Payment method Create API ([#5812](https://github.com/juspay/hyperswitch/pull/5812)) ([`be902ff`](https://github.com/juspay/hyperswitch/commit/be902ffa5328d32efe70c40c36f86d8fbfa01c79)) +- **routing:** Build gRPC Client Interface to initiate communication with other gRPC services ([#5835](https://github.com/juspay/hyperswitch/pull/5835)) ([`99f5933`](https://github.com/juspay/hyperswitch/commit/99f593389451c13e81ba6d019e9f3ca4361942ed)) + +### Bug Fixes + +- **merchant_account_v2:** Remove compatible_connector field in metadata ([#5935](https://github.com/juspay/hyperswitch/pull/5935)) ([`8c99db7`](https://github.com/juspay/hyperswitch/commit/8c99db72adbd2b5f03c37fa5b1fa82b9c77ce2c5)) + +### Documentation + +- Add openapi docs for customers v2 ([#5926](https://github.com/juspay/hyperswitch/pull/5926)) ([`2bc8756`](https://github.com/juspay/hyperswitch/commit/2bc8756e061e9e0e705ce2c6b533c3583f3efa8b)) +- Api-reference changes for customers and admin list apis for v2 ([#5936](https://github.com/juspay/hyperswitch/pull/5936)) ([`ed13146`](https://github.com/juspay/hyperswitch/commit/ed13146b8088e1fcd6df8b820fa8c7b4a9e400a3)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`407c70c`](https://github.com/juspay/hyperswitch/commit/407c70cb151582ec3b2b661363811390dfeca8b2)) + +**Full Changelog:** [`2024.09.18.0...2024.09.19.0`](https://github.com/juspay/hyperswitch/compare/2024.09.18.0...2024.09.19.0) + +- - - + ## 2024.09.18.0 ### Features From 776b5482e9315e5040066ad9623509f3a0f10ce0 Mon Sep 17 00:00:00 2001 From: Swangi Kumari <85639103+swangi-kumari@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:29:24 +0530 Subject: [PATCH 21/48] refactor(router): handle connector errors for tax calculation flow (#5949) --- crates/hyperswitch_connectors/src/connectors/taxjar.rs | 2 +- .../src/connectors/taxjar/transformers.rs | 2 +- crates/router/src/core/payments/operations/tax_calculation.rs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/taxjar.rs b/crates/hyperswitch_connectors/src/connectors/taxjar.rs index 009a9cb0c97..56559b75abc 100644 --- a/crates/hyperswitch_connectors/src/connectors/taxjar.rs +++ b/crates/hyperswitch_connectors/src/connectors/taxjar.rs @@ -136,7 +136,7 @@ impl ConnectorCommon for Taxjar { Ok(ErrorResponse { status_code: res.status_code, - code: response.status.clone(), + code: response.error.clone(), message: response.detail.clone(), reason: Some(response.detail), attempt_status: None, diff --git a/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs b/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs index 53c120a2788..1bdc2d2049c 100644 --- a/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs @@ -183,7 +183,7 @@ impl #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] pub struct TaxjarErrorResponse { - pub status: String, + pub status: i64, pub error: String, pub detail: String, } diff --git a/crates/router/src/core/payments/operations/tax_calculation.rs b/crates/router/src/core/payments/operations/tax_calculation.rs index 64252762e6f..b30d1c45467 100644 --- a/crates/router/src/core/payments/operations/tax_calculation.rs +++ b/crates/router/src/core/payments/operations/tax_calculation.rs @@ -16,6 +16,7 @@ use crate::{ payments::{self, helpers, operations, PaymentData}, utils as core_utils, }, + db::errors::ConnectorErrorExt, routes::{app::ReqState, SessionState}, services, types::{ @@ -288,7 +289,7 @@ impl Domain Date: Thu, 19 Sep 2024 08:20:29 +0000 Subject: [PATCH 22/48] chore(version): 2024.09.19.1 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e966ee89b..a5c8a29d5c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.09.19.1 + +### Refactors + +- **router:** Handle connector errors for tax calculation flow ([#5949](https://github.com/juspay/hyperswitch/pull/5949)) ([`776b548`](https://github.com/juspay/hyperswitch/commit/776b5482e9315e5040066ad9623509f3a0f10ce0)) + +**Full Changelog:** [`2024.09.19.0...2024.09.19.1`](https://github.com/juspay/hyperswitch/compare/2024.09.19.0...2024.09.19.1) + +- - - + ## 2024.09.19.0 ### Features From 82574c0e8e7eb69e9f21eedc765145c724960cd5 Mon Sep 17 00:00:00 2001 From: Pa1NarK <69745008+pixincreate@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:40:09 +0530 Subject: [PATCH 23/48] feat(cypress-v2): add cypress v2 core flow tests (#5791) --- .github/CODEOWNERS | 1 + cypress-tests-v2/.gitignore | 8 + cypress-tests-v2/.prettierrc.json | 6 + cypress-tests-v2/README.md | 260 ++ cypress-tests-v2/cypress.config.js | 67 + .../cypress/e2e/configs/Payment/Commons.js | 1299 +++++++ .../cypress/e2e/configs/Payment/Utils.js | 108 + .../cypress/e2e/configs/Payment/_Reusable.js | 92 + .../e2e/configs/PaymentMethodList/placeholder | 0 .../cypress/e2e/configs/Payout/placeholder | 0 .../e2e/configs/Routing/AdvancedConfigs.js | 80 + .../e2e/configs/Routing/FallbackConfigs.js | 1 + .../cypress/e2e/configs/Routing/Utils.js | 35 + .../e2e/spec/Payment/0000-CoreFlows.cy.js | 153 + .../0000-PaymentmethodList.cy.js | 5 + .../e2e/spec/Payout/0000-CoreFlows.cy.js | 5 + .../e2e/spec/Routing/0000-Coreflows.cy.js | 123 + .../spec/Routing/0001-PriorityRouting.cy.js | 5 + .../cypress/fixtures/api_key.json | 11 + .../cypress/fixtures/business_profile.json | 62 + cypress-tests-v2/cypress/fixtures/imports.js | 15 + .../cypress/fixtures/merchant_account.json | 9 + .../fixtures/merchant_connector_account.json | 32 + .../cypress/fixtures/organization.json | 11 + .../cypress/fixtures/routing.json | 15 + cypress-tests-v2/cypress/support/commands.js | 1247 ++++++ cypress-tests-v2/cypress/support/e2e.js | 21 + .../cypress/support/redirectionHandler.js | 476 +++ cypress-tests-v2/cypress/utils/State.js | 24 + cypress-tests-v2/package-lock.json | 3456 +++++++++++++++++ cypress-tests-v2/package.json | 23 + cypress-tests/.prettierrc.json | 3 +- 32 files changed, 7652 insertions(+), 1 deletion(-) create mode 100644 cypress-tests-v2/.gitignore create mode 100644 cypress-tests-v2/.prettierrc.json create mode 100644 cypress-tests-v2/README.md create mode 100644 cypress-tests-v2/cypress.config.js create mode 100644 cypress-tests-v2/cypress/e2e/configs/Payment/Commons.js create mode 100644 cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js create mode 100644 cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js create mode 100644 cypress-tests-v2/cypress/e2e/configs/PaymentMethodList/placeholder create mode 100644 cypress-tests-v2/cypress/e2e/configs/Payout/placeholder create mode 100644 cypress-tests-v2/cypress/e2e/configs/Routing/AdvancedConfigs.js create mode 100644 cypress-tests-v2/cypress/e2e/configs/Routing/FallbackConfigs.js create mode 100644 cypress-tests-v2/cypress/e2e/configs/Routing/Utils.js create mode 100644 cypress-tests-v2/cypress/e2e/spec/Payment/0000-CoreFlows.cy.js create mode 100644 cypress-tests-v2/cypress/e2e/spec/PaymentMethodList/0000-PaymentmethodList.cy.js create mode 100644 cypress-tests-v2/cypress/e2e/spec/Payout/0000-CoreFlows.cy.js create mode 100644 cypress-tests-v2/cypress/e2e/spec/Routing/0000-Coreflows.cy.js create mode 100644 cypress-tests-v2/cypress/e2e/spec/Routing/0001-PriorityRouting.cy.js create mode 100644 cypress-tests-v2/cypress/fixtures/api_key.json create mode 100644 cypress-tests-v2/cypress/fixtures/business_profile.json create mode 100644 cypress-tests-v2/cypress/fixtures/imports.js create mode 100644 cypress-tests-v2/cypress/fixtures/merchant_account.json create mode 100644 cypress-tests-v2/cypress/fixtures/merchant_connector_account.json create mode 100644 cypress-tests-v2/cypress/fixtures/organization.json create mode 100644 cypress-tests-v2/cypress/fixtures/routing.json create mode 100644 cypress-tests-v2/cypress/support/commands.js create mode 100644 cypress-tests-v2/cypress/support/e2e.js create mode 100644 cypress-tests-v2/cypress/support/redirectionHandler.js create mode 100644 cypress-tests-v2/cypress/utils/State.js create mode 100644 cypress-tests-v2/package-lock.json create mode 100644 cypress-tests-v2/package.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 88bf8b28ab6..e66da85d6c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -25,6 +25,7 @@ Cargo.lock @juspay/hyperswitch-framework postman/ @juspay/hyperswitch-qa cypress-tests/ @juspay/hyperswitch-qa +cypress-tests-v2/ @juspay/hyperswitch-qa crates/api_models/src/events/ @juspay/hyperswitch-analytics crates/api_models/src/events.rs @juspay/hyperswitch-analytics diff --git a/cypress-tests-v2/.gitignore b/cypress-tests-v2/.gitignore new file mode 100644 index 00000000000..4d9625ebe20 --- /dev/null +++ b/cypress-tests-v2/.gitignore @@ -0,0 +1,8 @@ +# Outputs +reports +screenshots + +# Files +creds.json +rotate-password.sh +run_all.sh diff --git a/cypress-tests-v2/.prettierrc.json b/cypress-tests-v2/.prettierrc.json new file mode 100644 index 00000000000..f0eb61e0f7c --- /dev/null +++ b/cypress-tests-v2/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": false +} diff --git a/cypress-tests-v2/README.md b/cypress-tests-v2/README.md new file mode 100644 index 00000000000..dccb66ed0e5 --- /dev/null +++ b/cypress-tests-v2/README.md @@ -0,0 +1,260 @@ +# Cypress Tests + +## Overview + +This Tool is a solution designed to automate testing for the [Hyperswitch](https://github.com/juspay/hyperswitch/) using Cypress, an open-source tool capable of conducting API call tests and UI tests. This README provides guidance on installing Cypress and its dependencies. + +## Installation + +### Prerequisites + +Before installing Cypress, ensure you have the following prerequisites installed: + +- npm (Node Package Manager) +- Node.js (18.x and above) + +### Run Test Cases on your local + +To run test cases, follow these steps: + +1. Clone the repository and switch to the project directory: + + ```shell + git clone https://github.com/juspay/hyperswitch + cd cypress-tests + ``` + +2. Install Cypress and its dependencies to `cypress-tests` directory by running the following command: + + ```shell + npm install + ``` + +3. Set environment variables for cypress + + ```shell + export CYPRESS_CONNECTOR="connector_id" + export CYPRESS_BASEURL="base_url" + export DEBUG=cypress:cli + export CYPRESS_ADMINAPIKEY="admin_api_key" + export CYPRESS_CONNECTOR_AUTH_FILE_PATH="path/to/creds.json" + ``` + +4. Run Cypress test cases + + To run the tests in interactive mode run the following command + + ```shell + npm run cypress + ``` + + To run all the tests in headless mode run the following command + + ```shell + npm run cypress:ci + ``` + + To run payment tests in headless mode run the following command + + ```shell + npm run cypress:payments + ``` + + To run payout tests in headless mode run the following command + + ```shell + npm run cypress:payouts + ``` + + To run routing tests in headless mode run the following command + + ```shell + npm run cypress:routing + ``` + +> [!NOTE] +> To learn about how creds file should be structured, refer to the [example.creds.json](#example-credsjson) section below. + +## Folder Structure + +The folder structure of this directory is as follows: + +```text +. # The root directory for the Cypress tests. +├── .gitignore +├── cypress # Contains Cypress-related files and folders. +│ ├── e2e # End-to-end test directory. +│ │ ├── ConnectorTest # Directory for test scenarios related to connectors. +│ │ │ ├── your_testcase1_files_here.cy.js +│ │ │ ├── your_testcase2_files_here.cy.js +│ │ │ └── ... +│ │ └── ConnectorUtils # Directory for utility functions related to connectors. +│ │ ├── connector_detail_files_here.js +│ │ └── utils.js +│ ├── fixtures # Directory for storing test data API request. +│ │ └── your_fixture_files_here.json +│ ├── support # Directory for Cypress support files. +│ │ ├── commands.js # File containing custom Cypress commands and utilities. +│ │ └── e2e.js +│ └── utils +│ └── utility_files_go_here.js +├── cypress.config.js # Cypress configuration file. +├── cypress.env.json # File is used to store environment-specific configuration values,such as base URLs, which can be accessed within your Cypress tests. +├── package.json # Node.js package file. +├── readme.md # This file +└── yarn.lock +``` + +## Writing Tests + +### Adding Connectors + +To add a new connector for testing with Hyperswitch, follow these steps: + +1.Include the connector details in the `creds.json` file: + +example: + +```json +{ + "stripe": { + "auth_type": "HeaderKey", + "api_key": "SK_134" + } +} +``` + +2.Add the new connector details to the ConnectorUtils folder (including CardNo and connector-specific information). + +Refer to Stripe.js file for guidance: + +```javascript +/cypress-tests/cypress/e2e/ConnectorUtils/Stripe.js +``` + +Similarly, create a new file named newconnectorname.js and include all the relevant information for that connector. + +3.In util.js, import the new connector details. + +### Adding Functions + +Similarly, add any helper functions or utilities in the `command.js` in support folder and import them into your tests as needed. + +Example: Adding List Mandate function to support `ListMandate` scenario + +```javascript +Cypress.Commands.add("listMandateCallTest", (globalState) => { + const customerId = globalState.get("customerId"); + cy.request({ + method: "GET", + url: `${globalState.get("baseUrl")}/customers/${customerId}/mandates`, + headers: { + "Content-Type": "application/json", + "api-key": globalState.get("apiKey"), + }, + }).then((response) => { + const xRequestId = response.headers["x-request-id"]; + if (xRequestId) { + cy.task("cli_log", "x-request-id ->> " + xRequestId); + } else { + cy.task( + "cli_log", + "x-request-id is not available in the response headers" + ); + } + expect(response.headers["content-type"]).to.include("application/json"); + console.log(response.body); + let i = 0; + for (i in response.body) { + if (response.body[i].mandate_id === globalState.get("mandateId")) { + expect(response.body[i].status).to.equal("active"); + } + } + }); +}); +``` + +### Adding Scenarios + +To add new test scenarios: + +1. Navigate to the ConnectorTest directory. +2. Create a new test file or modify existing ones to add your scenarios. +3. Write your test scenarios using Cypress commands. + +For example, to add a scenario for listing mandates in the `Mandateflows`: + +```javascript +// cypress/ConnectorTest/CreateSingleuseMandate.js +describe("Payment Scenarios", () => { + it("should complete a successful payment", () => { + // Your test logic here + }); +}); +``` + +In this scenario, you can call functions defined in `command.js`. For instance, to test the `listMandateCallTest` function: + +```javascript +describe("Payment Scenarios", () => { + it("list-mandate-call-test", () => { + cy.listMandateCallTest(globalState); + }); +}); +``` + +You can create similar scenarios by calling other functions defined in `command.js`. These functions interact with utility files like `connector.js` and include necessary assertions to support various connector scenarios. + +## Additional Resources + +For more information on using Cypress and writing effective tests, refer to the official Cypress documentation: [Cypress Documentation](https://docs.cypress.io/) + +## Example creds.json + +```json +{ + "adyen": { + "auth_type": "SignatureKey", + "api_key": "api_key", + "key1": "key1", + "api_secret": "api_secret" + }, + "bankofamerica": { + "auth_type": "SignatureKey", + "api_key": "api_key", + "key1": "key1", + "api_secret": "api_secret" + }, + "bluesnap": { + "auth_type": "BodyKey", + "api_key": "api_key", + "key1": "key1" + }, + "cybersource": { + "auth_type": "SignatureKey", + "api_key": "api_key", + "key1": "key1", + "api_secret": "api_secret" + }, + "nmi": { + "auth_type": "BodyKey", + "api_key": "api_key", + "key1": "key1" + }, + "paypal": { + "auth_type": "BodyKey", + "api_key": "api_key", + "key1": "key1" + }, + "stripe": { + "auth_type": "HeaderKey", + "api_key": "api_key" + }, + "trustpay": { + "auth_type": "SignatureKey", + "api_key": "api_key", + "key1": "key1", + "api_secret": "api_secret" + } +} +``` diff --git a/cypress-tests-v2/cypress.config.js b/cypress-tests-v2/cypress.config.js new file mode 100644 index 00000000000..7115b488658 --- /dev/null +++ b/cypress-tests-v2/cypress.config.js @@ -0,0 +1,67 @@ +const { defineConfig } = require("cypress"); +const fs = require("fs-extra"); +const path = require("path"); + +let globalState; +// Fetch from environment variable +const connectorId = process.env.CYPRESS_CONNECTOR || "service"; +const reportName = process.env.REPORT_NAME || `${connectorId}_report`; + +module.exports = defineConfig({ + e2e: { + setupNodeEvents(on, config) { + require("cypress-mochawesome-reporter/plugin")(on); + + on("task", { + setGlobalState: (val) => { + return (globalState = val || {}); + }, + getGlobalState: () => { + return globalState || {}; + }, + cli_log: (message) => { + console.log("Logging console message from task"); + console.log(message); + return null; + }, + }); + on("after:screenshot", async (details) => { + // Full path to the screenshot file + const screenshotPath = details.path; + + // Extract filename without extension + const name = path.basename( + screenshotPath, + path.extname(screenshotPath) + ); + + // Define a new name with a connectorId + const newName = `[${connectorId}] ${name}.png`; + const newPath = path.join(path.dirname(screenshotPath), newName); + + try { + await fs.rename(screenshotPath, newPath); + console.log("Screenshot renamed successfully"); + return { path: newPath }; + } catch (err) { + console.error("Failed to rename screenshot:", err); + } + }); + }, + experimentalRunAllSpecs: true, + + reporter: "cypress-mochawesome-reporter", + reporterOptions: { + reportDir: "cypress/reports", + reportFilename: reportName, + reportPageTitle: `[${connectorId}] Cypress test report`, + embeddedScreenshots: true, + overwrite: false, + inlineAssets: true, + saveJson: true, + }, + }, + chromeWebSecurity: false, + defaultCommandTimeout: 10000, + pageLoadTimeout: 20000, +}); diff --git a/cypress-tests-v2/cypress/e2e/configs/Payment/Commons.js b/cypress-tests-v2/cypress/e2e/configs/Payment/Commons.js new file mode 100644 index 00000000000..ea7b4f09fd7 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/configs/Payment/Commons.js @@ -0,0 +1,1299 @@ +// This file is the default. To override, add to connector.js + +import { getCustomExchange } from "./_Reusable"; + +const successfulNo3DSCardDetails = { + card_number: "4111111111111111", + card_exp_month: "08", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "999", +}; + +const successfulThreeDSTestCardDetails = { + card_number: "4111111111111111", + card_exp_month: "10", + card_exp_year: "25", + card_holder_name: "morino", + card_cvc: "999", +}; + +const singleUseMandateData = { + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "125.0.0.1", + user_agent: "amet irure esse", + }, + }, + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, +}; + +const multiUseMandateData = { + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "125.0.0.1", + user_agent: "amet irure esse", + }, + }, + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, +}; + +export const payment_methods_enabled = [ + { + payment_method: "bank_debit", + payment_method_types: [ + { + payment_method_type: "ach", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "bacs", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "becs", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "sepa", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + ], + }, + { + payment_method: "bank_redirect", + payment_method_types: [ + { + payment_method_type: "blik", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "eps", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "ideal", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "giropay", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "local_bank_redirect", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "przelewy24", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "sofort", + payment_experience: null, + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + ], + }, + { + payment_method: "bank_transfer", + payment_method_types: [ + { + payment_method_type: "ach", + payment_experience: "redirect_to_url", + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "bacs", + payment_experience: "redirect_to_url", + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + { + payment_method_type: "pix", + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: false, + installment_payment_enabled: true, + }, + { + payment_method_type: "sepa", + payment_experience: "redirect_to_url", + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + ], + }, + { + payment_method: "card", + payment_method_types: [ + { + payment_method_type: "credit", + card_networks: [ + "AmericanExpress", + "Discover", + "Interac", + "JCB", + "Mastercard", + "Visa", + "DinersClub", + "UnionPay", + "RuPay", + ], + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: false, + installment_payment_enabled: true, + }, + { + payment_method_type: "debit", + card_networks: [ + "AmericanExpress", + "Discover", + "Interac", + "JCB", + "Mastercard", + "Visa", + "DinersClub", + "UnionPay", + "RuPay", + ], + minimum_amount: -1, + maximum_amount: 68607706, + recurring_enabled: false, + installment_payment_enabled: true, + }, + ], + }, + { + payment_method: "card_redirect", + payment_method_types: [ + { + payment_method_type: "card_redirect", + payment_experience: "redirect_to_url", + card_networks: null, + accepted_currencies: null, + accepted_countries: null, + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + }, + ], + }, + { + payment_method: "real_time_payment", + payment_method_types: [ + { + payment_method_type: "duit_now", + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + payment_experience: "redirect_to_url", + }, + { + payment_method_type: "fps", + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + payment_experience: "redirect_to_url", + }, + { + payment_method_type: "prompt_pay", + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + payment_experience: "redirect_to_url", + }, + { + payment_method_type: "viet_qr", + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + payment_experience: "redirect_to_url", + }, + ], + }, + { + payment_method: "upi", + payment_method_types: [ + { + payment_method_type: "upi_collect", + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + payment_experience: "redirect_to_url", + }, + { + payment_method_type: "upi_intent", + minimum_amount: 1, + maximum_amount: 68607706, + recurring_enabled: true, + installment_payment_enabled: true, + payment_experience: "redirect_to_url", + }, + ], + }, +]; + +export const connectorDetails = { + bank_transfer_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "BRL", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + Pix: getCustomExchange({ + Request: { + payment_method: "bank_transfer", + payment_method_type: "pix", + payment_method_data: { + bank_transfer: { + pix: {}, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "BR", + first_name: "john", + last_name: "doe", + }, + }, + currency: "BRL", + }, + }), + }, + bank_redirect_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "EUR", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + Ideal: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "ideal", + payment_method_data: { + bank_redirect: { + ideal: { + bank_name: "ing", + country: "NL", + }, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "NL", + first_name: "john", + last_name: "doe", + }, + }, + }, + }), + Giropay: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "giropay", + payment_method_data: { + bank_redirect: { + giropay: { + bank_name: "", + bank_account_bic: "", + bank_account_iban: "", + preferred_language: "en", + country: "DE", + }, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "DE", + first_name: "john", + last_name: "doe", + }, + }, + }, + }), + Sofort: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "sofort", + payment_method_data: { + bank_redirect: { + sofort: { + country: "DE", + preferred_language: "en", + }, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "DE", + first_name: "john", + last_name: "doe", + }, + }, + }, + }), + Eps: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "eps", + payment_method_data: { + bank_redirect: { + eps: { + bank_name: "ing", + }, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "AT", + first_name: "john", + last_name: "doe", + }, + }, + }, + }), + Przelewy24: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "przelewy24", + payment_method_data: { + bank_redirect: { + przelewy24: { + bank_name: "citi", + billing_details: { + email: "guest@juspay.in", + }, + }, + }, + }, + }, + }), + BlikPaymentIntent: getCustomExchange({ + Request: { + currency: "PLN", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + Blik: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "blik", + payment_method_data: { + bank_redirect: { + blik: { + blik_code: "777987", + }, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "PL", + first_name: "john", + last_name: "doe", + }, + }, + }, + }), + }, + card_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + PaymentIntentOffSession: getCustomExchange({ + Request: { + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + "3DSManualCapture": getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + }), + "3DSAutoCapture": getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + }), + No3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + }), + No3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + }), + Capture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }), + PartialCapture: getCustomExchange({ + Request: {}, + }), + Void: getCustomExchange({ + Request: {}, + Response: { + status: 200, + body: { + status: "cancelled", + capture_method: "manual", + }, + }, + ResponseCustom: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "You cannot cancel this payment because it has status succeeded", + code: "IR_16", + }, + }, + }, + }), + Refund: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + ResponseCustom: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "The refund amount exceeds the amount captured", + code: "IR_13", + }, + }, + }, + }), + PartialRefund: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }), + SyncRefund: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }), + MandateSingleUse3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + mandate_data: singleUseMandateData, + }, + }), + MandateSingleUse3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + mandate_data: singleUseMandateData, + }, + }), + MandateSingleUseNo3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: singleUseMandateData, + }, + }), + MandateSingleUseNo3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: singleUseMandateData, + }, + }), + MandateMultiUseNo3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: multiUseMandateData, + }, + }), + MandateMultiUseNo3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: multiUseMandateData, + }, + }), + MandateMultiUse3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + mandate_data: multiUseMandateData, + }, + }), + MandateMultiUse3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + mandate_data: multiUseMandateData, + }, + }), + ZeroAuthMandate: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: singleUseMandateData, + }, + }), + SaveCardUseNo3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + SaveCardUseNo3DSAutoCaptureOffSession: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + setup_future_usage: "off_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + SaveCardUseNo3DSManualCaptureOffSession: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + setup_future_usage: "off_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + SaveCardConfirmAutoCaptureOffSession: getCustomExchange({ + Request: { + setup_future_usage: "off_session", + }, + }), + SaveCardConfirmManualCaptureOffSession: getCustomExchange({ + Request: { + setup_future_usage: "off_session", + }, + }), + SaveCardUseNo3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + PaymentMethodIdMandateNo3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: null, + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "125.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + PaymentMethodIdMandateNo3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + mandate_data: null, + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "125.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + PaymentMethodIdMandate3DSAutoCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + currency: "USD", + mandate_data: null, + authentication_type: "three_ds", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "125.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + PaymentMethodIdMandate3DSManualCapture: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + mandate_data: null, + authentication_type: "three_ds", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "125.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + InvalidCardNumber: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "123456", + card_exp_month: "10", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: "Json deserialize error: invalid card number length", + }, + }, + }, + InvalidExpiryMonth: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "00", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Invalid Expiry Month", + code: "IR_16", + }, + }, + }, + }, + InvalidExpiryYear: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Invalid Expiry Year", + code: "IR_16", + }, + }, + }, + }, + InvalidCardCvv: { + Request: { + currency: "USD", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123456", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Invalid card_cvc length", + code: "IR_16", + }, + }, + }, + }, + InvalidCurrency: { + Request: { + currency: "United", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123456", + }, + }, + }, + Response: { + status: 400, + body: { + error: + "Json deserialize error: unknown variant `United`, expected one of `AED`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BWP`, `BYN`, `BZD`, `CAD`, `CHF`, `CLP`, `CNY`, `COP`, `CRC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SEK`, `SGD`, `SHP`, `SLE`, `SLL`, `SOS`, `SRD`, `SSP`, `STN`, `SVC`, `SZL`, `THB`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`", + }, + }, + }, + InvalidCaptureMethod: { + Request: { + currency: "USD", + capture_method: "auto", + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123456", + }, + }, + }, + Response: { + status: 400, + body: { + error: + "Json deserialize error: unknown variant `auto`, expected one of `automatic`, `manual`, `manual_multiple`, `scheduled`", + }, + }, + }, + InvalidPaymentMethod: { + Request: { + currency: "USD", + payment_method: "this_supposed_to_be_a_card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2023", + card_holder_name: "joseph Doe", + card_cvc: "123456", + }, + }, + }, + Response: { + status: 400, + body: { + error: + "Json deserialize error: unknown variant `this_supposed_to_be_a_card`, expected one of `card`, `card_redirect`, `pay_later`, `wallet`, `bank_redirect`, `bank_transfer`, `crypto`, `bank_debit`, `reward`, `real_time_payment`, `upi`, `voucher`, `gift_card`, `open_banking`", + }, + }, + }, + InvalidAmountToCapture: { + Request: { + currency: "USD", + amount_to_capture: 10000, + payment_method: "card", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2026", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "amount_to_capture contains invalid data. Expected format is amount_to_capture lesser than amount", + code: "IR_05", + }, + }, + }, + }, + MissingRequiredParam: { + Request: { + currency: "USD", + payment_method_type: "debit", + setup_future_usage: "on_session", + payment_method_data: { + card: { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "2026", + card_holder_name: "joseph Doe", + card_cvc: "123", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Missing required param: payment_method", + code: "IR_04", + }, + }, + }, + }, + PaymentIntentErrored: { + Request: { + currency: "USD", + }, + Response: { + status: 422, + body: { + error: { + type: "invalid_request", + message: "A payment token or payment method data is required", + code: "IR_06", + }, + }, + }, + }, + CaptureGreaterAmount: { + Request: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "amount_to_capture is greater than amount", + code: "IR_06", + }, + }, + }, + }, + CaptureCapturedAmount: getCustomExchange({ + Request: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be captured because it has a payment.status of succeeded. The expected state is requires_capture, partially_captured_and_capturable, processing", + code: "IR_14", + }, + }, + }, + }), + ConfirmSuccessfulPayment: getCustomExchange({ + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "You cannot confirm this payment because it has status succeeded", + code: "IR_16", + }, + }, + }, + }), + }, + upi_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "INR", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + UpiCollect: getCustomExchange({ + Request: { + payment_method: "upi", + payment_method_type: "upi_collect", + payment_method_data: { + upi: { + upi_collect: { + vpa_id: "successtest@iata", + }, + }, + }, + }, + }), + UpiIntent: getCustomExchange({ + Request: { + payment_method: "upi", + payment_method_type: "upi_intent", + payment_method_data: { + upi: { + upi_intent: {}, + }, + }, + }, + }), + }, +}; diff --git a/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js b/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js new file mode 100644 index 00000000000..6faea73d4ef --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/configs/Payment/Utils.js @@ -0,0 +1,108 @@ +import { connectorDetails as CommonConnectorDetails } from "./Commons.js"; + +const connectorDetails = { + commons: CommonConnectorDetails, +}; + +export default function getConnectorDetails(connectorId) { + let x = mergeDetails(connectorId); + return x; +} + +function mergeDetails(connectorId) { + const connectorData = getValueByKey(connectorDetails, connectorId); + const fallbackData = getValueByKey(connectorDetails, "commons"); + // Merge data, prioritizing connectorData and filling missing data from fallbackData + const mergedDetails = mergeConnectorDetails(connectorData, fallbackData); + return mergedDetails; +} + +function mergeConnectorDetails(source, fallback) { + const merged = {}; + + // Loop through each key in the source object + for (const key in source) { + merged[key] = { ...source[key] }; // Copy properties from source + + // Check if fallback has the same key and properties are missing in source + if (fallback[key]) { + for (const subKey in fallback[key]) { + if (!merged[key][subKey]) { + merged[key][subKey] = fallback[key][subKey]; + } + } + } + } + + // Add missing keys from fallback that are not present in source + for (const key in fallback) { + if (!merged[key]) { + merged[key] = fallback[key]; + } + } + + return merged; +} + +export function getValueByKey(jsonObject, key) { + const data = + typeof jsonObject === "string" ? JSON.parse(jsonObject) : jsonObject; + + if (data && typeof data === "object" && key in data) { + return data[key]; + } else { + return null; + } +} + +export const should_continue_further = (res_data) => { + if (res_data.trigger_skip !== undefined) { + return !res_data.trigger_skip; + } + + if ( + res_data.body.error !== undefined || + res_data.body.error_code !== undefined || + res_data.body.error_message !== undefined + ) { + return false; + } else { + return true; + } +}; + +export function defaultErrorHandler(response, response_data) { + if ( + response.status === 400 && + response.body.error.message === "Payment method type not supported" + ) { + // Update the default status from 501 to 400 as `unsupported payment method` error is the next common error after `not implemented` error + response_data = updateDefaultStatusCode(); + } + + if (response_data.status === 200) { + throw new Error("Expecting valid response but got an error response"); + } + + expect(response.body).to.have.property("error"); + + if (typeof response.body.error === "object") { + for (const key in response_data.body.error) { + expect(response_data.body.error[key]).to.equal(response.body.error[key]); + } + } else if (typeof response.body.error === "string") { + expect(response.body.error).to.include(response_data.body.error); + } +} + +export function isoTimeTomorrow() { + const now = new Date(); + + // Create a new date object for tomorrow + const tomorrow = new Date(now); + tomorrow.setDate(now.getDate() + 1); + + // Convert to ISO string format + const isoStringTomorrow = tomorrow.toISOString(); + return isoStringTomorrow; +} diff --git a/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js b/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js new file mode 100644 index 00000000000..b69506ff075 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/configs/Payment/_Reusable.js @@ -0,0 +1,92 @@ +import State from "../../../utils/State"; +const globalState = new State({ + connectorId: Cypress.env("CONNECTOR"), + baseUrl: Cypress.env("BASEURL"), + adminApiKey: Cypress.env("ADMINAPIKEY"), + connectorAuthFilePath: Cypress.env("CONNECTOR_AUTH_FILE_PATH"), +}); + +const connectorName = normalise(globalState.get("connectorId")); + +function normalise(input) { + const exceptions = { + bankofamerica: "Bank of America", + cybersource: "Cybersource", + paybox: "Paybox", + paypal: "Paypal", + wellsfargo: "Wellsfargo", + // Add more known exceptions here + }; + + if (typeof input !== "string") { + const spec_name = Cypress.spec.name.split("-")[1].split(".")[0]; + return `${spec_name}`; + } + + const lowerCaseInput = input.toLowerCase(); + return exceptions[lowerCaseInput] || input; +} + +/* +`getDefaultExchange` contains the default Request and Response to be considered if none provided. +`getCustomExchange` takes in 2 optional fields named as Request and Response. +with `getCustomExchange`, if 501 response is expected, there is no need to pass Response as it considers default values. +*/ + +// Const to get default PaymentExchange object +const getDefaultExchange = () => ({ + Request: { + currency: "EUR", + }, + Response: { + status: 501, + body: { + error: { + type: "invalid_request", + message: `Selected payment method through ${connectorName} is not implemented`, + code: "IR_00", + }, + }, + }, +}); + +const getUnsupportedExchange = () => ({ + Request: { + currency: "EUR", + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: `Payment method type not supported`, + code: "IR_19", + }, + }, + }, +}); + +// Const to get PaymentExchange with overridden properties +export const getCustomExchange = (overrides) => { + const defaultExchange = getDefaultExchange(); + + return { + ...defaultExchange, + Request: { + ...defaultExchange.Request, + ...(overrides.Request || {}), + }, + Response: { + ...defaultExchange.Response, + ...(overrides.Response || {}), + }, + ...(overrides.ResponseCustom + ? { ResponseCustom: overrides.ResponseCustom } + : {}), + }; +}; + +// Function to update the default status code +export const updateDefaultStatusCode = () => { + return getUnsupportedExchange().Response; +}; diff --git a/cypress-tests-v2/cypress/e2e/configs/PaymentMethodList/placeholder b/cypress-tests-v2/cypress/e2e/configs/PaymentMethodList/placeholder new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cypress-tests-v2/cypress/e2e/configs/Payout/placeholder b/cypress-tests-v2/cypress/e2e/configs/Payout/placeholder new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cypress-tests-v2/cypress/e2e/configs/Routing/AdvancedConfigs.js b/cypress-tests-v2/cypress/e2e/configs/Routing/AdvancedConfigs.js new file mode 100644 index 00000000000..fbed06301aa --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/configs/Routing/AdvancedConfigs.js @@ -0,0 +1,80 @@ +const data = [ + { + connector: "adyen", + merchant_connector_id: "", + }, +]; + +const rules = [ + { + name: "rule_1", + connectorSelection: { + type: "priority", + data: [ + { + connector: "stripe", + merchant_connector_id: "", + }, + { + connector: "bluesnap", + merchant_connector_id: "", + }, + ], + }, + statements: [ + { + condition: [ + { + lhs: "payment_method", + comparison: "equal", + value: { + type: "enum_variant", + value: "card", + }, + metadata: {}, + }, + ], + }, + ], + }, + { + name: "rule_2", + connectorSelection: { + type: "priority", + data: [ + { + connector: "adyen", + merchant_connector_id: "", + }, + ], + }, + statements: [ + { + condition: [ + { + lhs: "payment_method", + comparison: "equal", + value: { + type: "enum_variant", + value: "bank_redirect", + }, + metadata: {}, + }, + ], + }, + ], + }, +]; + +export const configs = { + name: "Rule Based routing", + description: "Advanced configuration (Rule based routing) for core flows.", + data: { + defaultSelection: { + type: "priority", + data: data, + }, + rules: rules, + metadata: {}, + }, +}; diff --git a/cypress-tests-v2/cypress/e2e/configs/Routing/FallbackConfigs.js b/cypress-tests-v2/cypress/e2e/configs/Routing/FallbackConfigs.js new file mode 100644 index 00000000000..610aed67bed --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/configs/Routing/FallbackConfigs.js @@ -0,0 +1 @@ +export const configs = ["bluesnap"]; diff --git a/cypress-tests-v2/cypress/e2e/configs/Routing/Utils.js b/cypress-tests-v2/cypress/e2e/configs/Routing/Utils.js new file mode 100644 index 00000000000..49725e8498d --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/configs/Routing/Utils.js @@ -0,0 +1,35 @@ +import { configs as advancedConfigs } from "./AdvancedConfigs.js"; +import { configs as fallbackConfigs } from "./FallbackConfigs.js"; + +const serviceDetails = { + advanced_configs: advancedConfigs, + fallback_configs: fallbackConfigs, +}; + +export const getServiceDetails = (serviceId) => { + let data = getValueByKey(serviceDetails, serviceId); + return data; +}; + +function getValueByKey(jsonObject, key) { + const data = + typeof jsonObject === "string" ? JSON.parse(jsonObject) : jsonObject; + + if (data && typeof data === "object" && key in data) { + return data[key]; + } else { + return null; + } +} + +export const should_continue_further = (res_data) => { + if ( + res_data.body.error !== undefined || + res_data.body.error_code !== undefined || + res_data.body.error_message !== undefined + ) { + return false; + } else { + return true; + } +}; diff --git a/cypress-tests-v2/cypress/e2e/spec/Payment/0000-CoreFlows.cy.js b/cypress-tests-v2/cypress/e2e/spec/Payment/0000-CoreFlows.cy.js new file mode 100644 index 00000000000..b2cbcf4a550 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/Payment/0000-CoreFlows.cy.js @@ -0,0 +1,153 @@ +import * as fixtures from "../../../fixtures/imports"; +import State from "../../../utils/State"; +import { payment_methods_enabled } from "../../configs/Payment/Commons"; + +let globalState; + +describe("Core APIs", () => { + context("Organization APIs", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("Organization create call", () => { + cy.organizationCreateCall( + fixtures.organization_body.org_create, + globalState + ); + }); + it("Organization retrieve call", () => { + cy.organizationRetrieveCall(globalState); + }); + it("Organization update call", () => { + cy.organizationUpdateCall( + fixtures.organization_body.org_update, + globalState + ); + }); + }); + + context("Merchant account APIs", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("Merchant account create call", () => { + cy.merchantAccountCreateCall( + fixtures.merchant_account_body.ma_create, + globalState + ); + }); + it("Merchant account retrieve call", () => { + cy.merchantAccountRetrieveCall(globalState); + }); + it("Merchant account update call", () => { + cy.merchantAccountUpdateCall( + fixtures.merchant_account_body.ma_update, + globalState + ); + }); + }); + + context("Business profile APIs", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("Business profile create call", () => { + cy.businessProfileCreateCall( + fixtures.business_profile_body.bp_create, + globalState + ); + }); + it("Business profile retrieve call", () => { + cy.businessProfileRetrieveCall(globalState); + }); + it("Business profile update call", () => { + cy.businessProfileUpdateCall( + fixtures.business_profile_body.bp_update, + globalState + ); + }); + }); + + context("Merchant connector account APIs", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("[Payment] Merchant connector account create call", () => { + // `globalState` can only be accessed in the `it` block + const connector_name = globalState.data.connectorId; + cy.mcaCreateCall( + `${connector_name}_default`, + connector_name, + "payment_processor", + globalState, + fixtures.merchant_connector_account_body.mca_create, + payment_methods_enabled + ); + }); + it("[Payment] Merchant connector account retrieve call", () => { + cy.mcaRetrieveCall(globalState); + }); + it("[Payment] Merchant connector account update call", () => { + // `globalState` can only be accessed in the `it` block + const connector_name = globalState.data.connectorId; + cy.mcaUpdateCall( + `${connector_name}_default`, + connector_name, + "payment_processor", + globalState, + fixtures.merchant_connector_account_body.mca_update, + payment_methods_enabled + ); + }); + }); + + context("API Key APIs", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("API Key create call", () => { + cy.apiKeyCreateCall(fixtures.api_key_body.api_key_create, globalState); + }); + it("API Key retrieve call", () => { + cy.apiKeyRetrieveCall(globalState); + }); + it("API Key update call", () => { + cy.apiKeyUpdateCall(fixtures.api_key_body.api_key_update, globalState); + }); + }); +}); diff --git a/cypress-tests-v2/cypress/e2e/spec/PaymentMethodList/0000-PaymentmethodList.cy.js b/cypress-tests-v2/cypress/e2e/spec/PaymentMethodList/0000-PaymentmethodList.cy.js new file mode 100644 index 00000000000..eb0012026b0 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/PaymentMethodList/0000-PaymentmethodList.cy.js @@ -0,0 +1,5 @@ +describe("template spec", () => { + it("passes", () => { + cy.visit("https://example.cypress.io"); + }); +}); diff --git a/cypress-tests-v2/cypress/e2e/spec/Payout/0000-CoreFlows.cy.js b/cypress-tests-v2/cypress/e2e/spec/Payout/0000-CoreFlows.cy.js new file mode 100644 index 00000000000..eb0012026b0 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/Payout/0000-CoreFlows.cy.js @@ -0,0 +1,5 @@ +describe("template spec", () => { + it("passes", () => { + cy.visit("https://example.cypress.io"); + }); +}); diff --git a/cypress-tests-v2/cypress/e2e/spec/Routing/0000-Coreflows.cy.js b/cypress-tests-v2/cypress/e2e/spec/Routing/0000-Coreflows.cy.js new file mode 100644 index 00000000000..cde64e563bb --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/Routing/0000-Coreflows.cy.js @@ -0,0 +1,123 @@ +import fixtures from "../../../fixtures/routing.json"; +import State from "../../../utils/State"; +import * as utils from "../../configs/Routing/Utils"; + +let globalState; + +// Marked as skipped as the List APIs are not implemented yet. +// In addition to this, we do not want to hard code the MCA Ids in the test cases. +describe("Routing core APIs", () => { + context("Login", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("User login", () => { + cy.userLogin(globalState); + cy.terminate2Fa(globalState); + cy.userInfo(globalState); + }); + }); + + context("Fetch MCA Ids", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("List MCA call", () => { + cy.listMcaCall(globalState); + }); + }); + + context("Routing APIs", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + it("Routing algorithm create call", () => { + const adyen_merchant_connector_id = + globalState.data.adyenMerchantConnectorId; + const bluesnap_merchant_connector_id = + globalState.data.bluesnapMerchantConnectorId; + const stripe_merchant_connector_id = + globalState.data.stripeMerchantConnectorId; + + // Fetching the advanced config details + const advanced_config_details = + utils.getServiceDetails("advanced_configs"); + // setting the merchant connector ids in the payload + // defaultSelection data + advanced_config_details[ + "data" + ].defaultSelection.data[0].merchant_connector_id = + adyen_merchant_connector_id; + // rules data + // rule 1 + advanced_config_details[ + "data" + ].rules[0].connectorSelection.data[0].merchant_connector_id = + stripe_merchant_connector_id; + advanced_config_details[ + "data" + ].rules[0].connectorSelection.data[1].merchant_connector_id = + bluesnap_merchant_connector_id; + // rule 2 + advanced_config_details[ + "data" + ].rules[1].connectorSelection.data[0].merchant_connector_id = + adyen_merchant_connector_id; + + const payload = { + name: advanced_config_details["name"], + data: advanced_config_details["data"], + description: advanced_config_details["description"], + }; + const type = "advanced"; + + cy.routingSetupCall(fixtures.routing_create, type, payload, globalState); + }); + it("Routing algorithm activate call", () => { + cy.routingActivateCall(fixtures.routing_activate, globalState); + }); + it("Routing algorithm activation retrieve call", () => { + cy.routingActivationRetrieveCall(globalState); + }); + it("Routing algorithm deactivate call", () => { + cy.routingDeactivateCall(globalState); + }); + it("Routing algorithm retrieve call", () => { + cy.routingRetrieveCall(globalState); + }); + it("Routing algorithm default fallback update call", () => { + //fallback_config_details + const payload = utils.getServiceDetails("fallback_configs"); + + cy.routingDefaultFallbackCall( + fixtures.default_fallback_update, + payload, + globalState + ); + }); + it("Routing algorithm fallback retrieve call", () => { + cy.routingFallbackRetrieveCall(globalState); + }); + }); +}); diff --git a/cypress-tests-v2/cypress/e2e/spec/Routing/0001-PriorityRouting.cy.js b/cypress-tests-v2/cypress/e2e/spec/Routing/0001-PriorityRouting.cy.js new file mode 100644 index 00000000000..eb0012026b0 --- /dev/null +++ b/cypress-tests-v2/cypress/e2e/spec/Routing/0001-PriorityRouting.cy.js @@ -0,0 +1,5 @@ +describe("template spec", () => { + it("passes", () => { + cy.visit("https://example.cypress.io"); + }); +}); diff --git a/cypress-tests-v2/cypress/fixtures/api_key.json b/cypress-tests-v2/cypress/fixtures/api_key.json new file mode 100644 index 00000000000..2dbb87e103c --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/api_key.json @@ -0,0 +1,11 @@ +{ + "api_key_create": { + "name": "Hyperswitch API Key", + "expiration": "", + "description": null + }, + "api_key_update": { + "description": "Hyperswitch Cypress specific API Key", + "expiration": "" + } +} diff --git a/cypress-tests-v2/cypress/fixtures/business_profile.json b/cypress-tests-v2/cypress/fixtures/business_profile.json new file mode 100644 index 00000000000..86410a03f63 --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/business_profile.json @@ -0,0 +1,62 @@ +{ + "bp_create": { + "profile_name": "HyperS", + "return_url": "https://duck.com/success", + "enable_payment_response_hash": true, + "redirect_to_merchant_with_http_post": false, + "metadata": null, + "order_fulfillment_time": 900, + "order_fulfillment_time_origin": "create", + "applepay_verified_domains": null, + "session_expiry": 900, + "payment_link_config": null, + "authentication_connector_details": null, + "use_billing_as_payment_method_billing": true, + "collect_shipping_details_from_wallet_connector_if_required": false, + "collect_billing_details_from_wallet_connector_if_required": false, + "always_collect_shipping_details_from_wallet_connector": false, + "always_collect_billing_details_from_wallet_connector": false, + "is_connector_agnostic_mit_enabled": false, + "payout_link_config": null, + "outgoing_webhook_custom_http_headers": null, + "webhook_details": { + "webhook_version": "1.0.1", + "webhook_username": "ekart_retail", + "webhook_password": "password_ekart@123", + "webhook_url": "https://webhook.site", + "payment_created_enabled": true, + "payment_succeeded_enabled": true, + "payment_failed_enabled": true + } + }, + "bp_update": { + "profile_name": "Hyperswitch", + "return_url": "https://duck.com/success", + "enable_payment_response_hash": true, + "redirect_to_merchant_with_http_post": false, + "metadata": null, + "order_fulfillment_time": 900, + "order_fulfillment_time_origin": "create", + "applepay_verified_domains": null, + "session_expiry": 900, + "payment_link_config": null, + "authentication_connector_details": null, + "use_billing_as_payment_method_billing": true, + "collect_shipping_details_from_wallet_connector_if_required": false, + "collect_billing_details_from_wallet_connector_if_required": false, + "always_collect_shipping_details_from_wallet_connector": false, + "always_collect_billing_details_from_wallet_connector": false, + "is_connector_agnostic_mit_enabled": false, + "payout_link_config": null, + "outgoing_webhook_custom_http_headers": null, + "webhook_details": { + "webhook_version": "1.0.1", + "webhook_username": "ekart_retail", + "webhook_password": "password_ekart@123", + "webhook_url": "https://webhook.site", + "payment_created_enabled": true, + "payment_succeeded_enabled": true, + "payment_failed_enabled": true + } + } +} diff --git a/cypress-tests-v2/cypress/fixtures/imports.js b/cypress-tests-v2/cypress/fixtures/imports.js new file mode 100644 index 00000000000..30f0dfb50c2 --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/imports.js @@ -0,0 +1,15 @@ +import api_key_body from "./api_key.json"; +import business_profile_body from "./business_profile.json"; +import merchant_account_body from "./merchant_account.json"; +import merchant_connector_account_body from "./merchant_connector_account.json"; +import organization_body from "./organization.json"; +import routing_body from "./routing.json"; + +export { + api_key_body, + business_profile_body, + merchant_account_body, + merchant_connector_account_body, + organization_body, + routing_body, +}; diff --git a/cypress-tests-v2/cypress/fixtures/merchant_account.json b/cypress-tests-v2/cypress/fixtures/merchant_account.json new file mode 100644 index 00000000000..ac24e956513 --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/merchant_account.json @@ -0,0 +1,9 @@ +{ + "ma_create": { + "merchant_name": "Hyperswitch Seller", + "organization_id": "" + }, + "ma_update": { + "merchant_name": "Hyperswitch" + } +} diff --git a/cypress-tests-v2/cypress/fixtures/merchant_connector_account.json b/cypress-tests-v2/cypress/fixtures/merchant_connector_account.json new file mode 100644 index 00000000000..ca9ada44a29 --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/merchant_connector_account.json @@ -0,0 +1,32 @@ +{ + "mca_create": { + "connector_type": "payment_processor", + "connector_name": "connector", + "connector_account_details": { + "auth_type": "auth_type", + "api_key": "", + "api_secret": "", + "key1": "", + "key2": "" + }, + "metadata": { + "status_url": "https://webhook.site", + "account_name": "transaction_processing", + "pricing_type": "fixed_price", + "acquirer_bin": "438309", + "acquirer_merchant_id": "00002000000" + }, + "frm_configs": null, + "connector_webhook_details": { + "merchant_secret": "" + }, + "profile_id": "" + }, + "mca_update": { + "connector_type": "payment_processor", + "connector_webhook_details": { + "merchant_secret": "m3rch4n7_se(rE7" + }, + "merchant_id": "" + } +} diff --git a/cypress-tests-v2/cypress/fixtures/organization.json b/cypress-tests-v2/cypress/fixtures/organization.json new file mode 100644 index 00000000000..24d084ab606 --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/organization.json @@ -0,0 +1,11 @@ +{ + "org_create": { + "organization_name": "Hyperswitch Organization" + }, + "org_update": { + "organization_name": "Hyperswitch", + "metadata": { + "additional_info": "123" + } + } +} diff --git a/cypress-tests-v2/cypress/fixtures/routing.json b/cypress-tests-v2/cypress/fixtures/routing.json new file mode 100644 index 00000000000..d8d5ee27caa --- /dev/null +++ b/cypress-tests-v2/cypress/fixtures/routing.json @@ -0,0 +1,15 @@ +{ + "routing_create": { + "name": "", + "description": "", + "algorithm": { + "type": "", + "data": {} + }, + "profile_id": "{{profile_id}}" + }, + "routing_activate": { + "routing_algorithm_id": "" + }, + "default_fallback_update": [] +} diff --git a/cypress-tests-v2/cypress/support/commands.js b/cypress-tests-v2/cypress/support/commands.js new file mode 100644 index 00000000000..d0a90d4106d --- /dev/null +++ b/cypress-tests-v2/cypress/support/commands.js @@ -0,0 +1,1247 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + +// cy.task can only be used in support files (spec files or commands file) + +import { + getValueByKey, + isoTimeTomorrow, +} from "../e2e/configs/Payment/Utils.js"; + +function logRequestId(xRequestId) { + if (xRequestId) { + cy.task("cli_log", "x-request-id: " + xRequestId); + } else { + cy.task("cli_log", "x-request-id is unavailable in the response headers"); + } +} + +// Organization API calls +Cypress.Commands.add( + "organizationCreateCall", + (organizationCreateBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const url = `${base_url}/v2/organization`; + + cy.request({ + method: "POST", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + }, + body: organizationCreateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body) + .to.have.property("organization_id") + .and.to.include("org_") + .and.to.be.a("string").and.not.be.empty; + globalState.set("organizationId", response.body.organization_id); + cy.task("setGlobalState", globalState.data); + expect(response.body).to.have.property("metadata").and.to.equal(null); + } else { + // to be updated + throw new Error( + `Organization create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); +Cypress.Commands.add("organizationRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const organization_id = globalState.get("organizationId"); + const url = `${base_url}/v2/organization/${organization_id}`; + + cy.request({ + method: "GET", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body) + .to.have.property("organization_id") + .and.to.include("org_") + .and.to.be.a("string").and.not.be.empty; + expect(response.body.organization_name) + .to.have.include("Hyperswitch") + .and.to.be.a("string").and.not.be.empty; + + if (organization_id === undefined || organization_id === null) { + globalState.set("organizationId", response.body.organization_id); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `Organization retrieve call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add( + "organizationUpdateCall", + (organizationUpdateBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const organization_id = globalState.get("organizationId"); + const url = `${base_url}/v2/organization/${organization_id}`; + + cy.request({ + method: "PUT", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + }, + body: organizationUpdateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body) + .to.have.property("organization_id") + .and.to.include("org_") + .and.to.be.a("string").and.not.be.empty; + expect(response.body).to.have.property("metadata").and.to.be.a("object") + .and.not.be.empty; + + if (organization_id === undefined || organization_id === null) { + globalState.set("organizationId", response.body.organization_id); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `Organization update call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); + +// Merchant account API calls +Cypress.Commands.add( + "merchantAccountCreateCall", + (merchantAccountCreateBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const organization_id = globalState.get("organizationId"); + const url = `${base_url}/v2/accounts`; + + const merchant_name = merchantAccountCreateBody.merchant_name + .replaceAll(" ", "") + .toLowerCase(); + + // Update request body + merchantAccountCreateBody.organization_id = organization_id; + + cy.request({ + method: "POST", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + }, + body: merchantAccountCreateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body) + .to.have.property("id") + .and.to.include(`${merchant_name}_`) + .and.to.be.a("string").and.not.be.empty; + + if (base_url.includes("sandbox") || base_url.includes("integ")) + expect(response.body) + .to.have.property("publishable_key") + .and.to.include("pk_snd").and.to.not.be.empty; + else if (base_url.includes("localhost")) + expect(response.body) + .to.have.property("publishable_key") + .and.to.include("pk_dev").and.to.not.be.empty; + + globalState.set("merchantId", response.body.id); + globalState.set("publishableKey", response.body.publishable_key); + + cy.task("setGlobalState", globalState.data); + } else { + // to be updated + throw new Error( + `Merchant create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); +Cypress.Commands.add("merchantAccountRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/accounts/${merchant_id}`; + + cy.request({ + method: "GET", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.have.property("id").and.to.be.a("string").and.not + .be.empty; + + if (base_url.includes("sandbox") || base_url.includes("integ")) + expect(response.body) + .to.have.property("publishable_key") + .and.to.include("pk_snd").and.to.not.be.empty; + else + expect(response.body) + .to.have.property("publishable_key") + .and.to.include("pk_dev").and.to.not.be.empty; + + if (merchant_id === undefined || merchant_id === null) { + globalState.set("merchantId", response.body.id); + globalState.set("publishableKey", response.body.publishable_key); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `Merchant retrieve call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add( + "merchantAccountUpdateCall", + (merchantAccountUpdateBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/accounts/${merchant_id}`; + + const merchant_name = merchantAccountUpdateBody.merchant_name; + + cy.request({ + method: "PUT", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + }, + body: merchantAccountUpdateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.id).to.equal(merchant_id); + + if (base_url.includes("sandbox") || base_url.includes("integ")) + expect(response.body) + .to.have.property("publishable_key") + .and.to.include("pk_snd").and.to.not.be.empty; + else + expect(response.body) + .to.have.property("publishable_key") + .and.to.include("pk_dev").and.to.not.be.empty; + expect(response.body.merchant_name).to.equal(merchant_name); + + if (merchant_id === undefined || merchant_id === null) { + globalState.set("merchantId", response.body.id); + globalState.set("publishableKey", response.body.publishable_key); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `Merchant update call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); + +// Business profile API calls +Cypress.Commands.add( + "businessProfileCreateCall", + (businessProfileCreateBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/profiles`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + cy.request({ + method: "POST", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + body: businessProfileCreateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.merchant_id).to.equal(merchant_id); + expect(response.body.id).to.include("pro_").and.to.not.be.empty; + expect(response.body.profile_name).to.equal( + businessProfileCreateBody.profile_name + ); + + globalState.set("profileId", response.body.id); + + cy.task("setGlobalState", globalState.data); + } else { + // to be updated + throw new Error( + `Merchant update call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); +Cypress.Commands.add("businessProfileRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/profiles/${profile_id}`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + cy.request({ + method: "GET", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.merchant_id).to.equal(merchant_id); + expect(response.body.id).to.include("pro_").and.to.not.be.empty; + + if (profile_id === undefined || profile_id === null) { + globalState.set("profileId", response.body.id); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `Merchant update call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add( + "businessProfileUpdateCall", + (businessProfileUpdateBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/profiles/${profile_id}`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + cy.request({ + method: "PUT", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + body: businessProfileUpdateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.merchant_id).to.equal(merchant_id); + expect(response.body.id).to.include("pro_").and.to.not.be.empty; + expect(response.body.profile_name).to.equal( + businessProfileUpdateBody.profile_name + ); + + if (profile_id === undefined || profile_id === null) { + globalState.set("profileId", response.body.id); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `Merchant update call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); + +// Merchant Connector Account API calls +// Payments API calls +Cypress.Commands.add( + "mcaCreateCall", + ( + connectorLabel, + connectorName, + connectorType, + globalState, + mcaCreateBody, + paymentMethodsEnabled + ) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/connector_accounts`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + // Update request body + mcaCreateBody.profile_id = profile_id; + mcaCreateBody.connector_label = connectorLabel; + mcaCreateBody.connector_name = connectorName; + mcaCreateBody.connector_type = connectorType; + mcaCreateBody.payment_methods_enabled = paymentMethodsEnabled; + + // readFile is used to read the contents of the file and it always returns a promise ([Object Object]) due to its asynchronous nature + // it is best to use then() to handle the response within the same block of code + cy.readFile(globalState.get("connectorAuthFilePath")).then( + (jsonContent) => { + const jsonString = JSON.stringify(jsonContent); + const key = + connectorType === "payment_processor" + ? connectorName + : `${connectorName}_payout`; + const authDetails = getValueByKey(jsonString, key); + + mcaCreateBody.connector_account_details = + authDetails.connector_account_details; + + if (authDetails && authDetails.metadata) { + createConnectorBody.metadata = { + ...createConnectorBody.metadata, // Preserve existing metadata fields + ...authDetails.metadata, // Merge with authDetails.metadata + }; + } + + cy.request({ + method: "POST", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + body: mcaCreateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.connector_name).to.equal(connectorName); + expect(response.body.id).to.include("mca_").and.to.not.be.empty; + expect(response.body.status).to.equal("active"); + expect(response.body.profile_id).to.equal(profile_id); + + globalState.set("merchantConnectorId", response.body.id); + + cy.task("setGlobalState", globalState.data); + } else { + // to be updated + throw new Error( + `MCA create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } + ); + } +); +Cypress.Commands.add("mcaRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const connector_name = globalState.get("connectorId"); + const merchant_connector_id = globalState.get("merchantConnectorId"); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/connector_accounts/${merchant_connector_id}`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + cy.request({ + method: "GET", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.connector_name).to.equal(connector_name); + expect(response.body.id).to.include("mca_").and.to.not.be.empty; + expect(response.body.status).to.equal("active"); + + if ( + merchant_connector_id === undefined || + merchant_connector_id === null + ) { + globalState.set("merchantConnectorId", response.body.id); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `MCA create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add( + "mcaUpdateCall", + ( + connectorLabel, + connectorName, + connectorType, + globalState, + mcaUpdateBody, + paymentMethodsEnabled + ) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_connector_id = globalState.get("merchantConnectorId"); + const merchant_id = globalState.get("merchantId"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/connector_accounts/${merchant_connector_id}`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + // Update request body + mcaUpdateBody.merchant_id = merchant_id; + mcaUpdateBody.connector_label = connectorLabel; + mcaUpdateBody.connector_type = connectorType; + mcaUpdateBody.payment_methods_enabled = paymentMethodsEnabled; + + cy.request({ + method: "PUT", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + body: mcaUpdateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.connector_name).to.equal(connectorName); + expect(response.body.id).to.include("mca_").and.to.not.be.empty; + expect(response.body.status).to.equal("active"); + expect(response.body.profile_id).to.equal(profile_id); + expect( + response.body.connector_webhook_details.merchant_secret + ).to.equal(mcaUpdateBody.connector_webhook_details.merchant_secret); + + if ( + merchant_connector_id === undefined || + merchant_connector_id === null + ) { + globalState.set("merchantConnectorId", response.body.id); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `MCA create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); + +// API Key API calls +Cypress.Commands.add("apiKeyCreateCall", (apiKeyCreateBody, globalState) => { + // Define the necessary variables and constant + + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + // We do not want to keep API Key forever, + // so we set the expiry to tomorrow as new merchant accounts are created with every run + const expiry = isoTimeTomorrow(); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/api_keys`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + // Update request body + apiKeyCreateBody.expiration = expiry; + + cy.request({ + method: "POST", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + body: apiKeyCreateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.merchant_id).to.equal(merchant_id); + expect(response.body.description).to.equal(apiKeyCreateBody.description); + + // API Key assertions are intentionally excluded to avoid being exposed in the logs + if (base_url.includes("sandbox") || base_url.includes("integ")) { + expect(response.body).to.have.property("key_id").and.to.include("snd_") + .and.to.not.be.empty; + } else if (base_url.includes("localhost")) { + expect(response.body).to.have.property("key_id").and.to.include("dev_") + .and.to.not.be.empty; + } + + globalState.set("apiKeyId", response.body.key_id); + globalState.set("apiKey", response.body.api_key); + + cy.task("setGlobalState", globalState.data); + } else { + // to be updated + throw new Error( + `API Key create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("apiKeyRetrieveCall", (globalState) => { + // Define the necessary variables and constant + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const api_key_id = globalState.get("apiKeyId"); + const url = `${base_url}/v2/api_keys/${api_key_id}`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + cy.request({ + method: "GET", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.merchant_id).to.equal(merchant_id); + + // API Key assertions are intentionally excluded to avoid being exposed in the logs + if (base_url.includes("sandbox") || base_url.includes("integ")) { + expect(response.body).to.have.property("key_id").and.to.include("snd_") + .and.to.not.be.empty; + } else if (base_url.includes("localhost")) { + expect(response.body).to.have.property("key_id").and.to.include("dev_") + .and.to.not.be.empty; + } + + if (api_key === undefined || api_key === null) { + globalState.set("apiKey", response.body.api_key); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `API Key create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("apiKeyUpdateCall", (apiKeyUpdateBody, globalState) => { + // Define the necessary variables and constant + const api_key = globalState.get("adminApiKey"); + const api_key_id = globalState.get("apiKeyId"); + const base_url = globalState.get("baseUrl"); + // We do not want to keep API Key forever, + // so we set the expiry to tomorrow as new merchant accounts are created with every run + const expiry = isoTimeTomorrow(); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/api_keys/${api_key_id}`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + // Update request body + apiKeyUpdateBody.expiration = expiry; + + cy.request({ + method: "PUT", + url: url, + headers: { + "Content-Type": "application/json", + "api-key": api_key, + ...customHeaders, + }, + body: apiKeyUpdateBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body.merchant_id).to.equal(merchant_id); + expect(response.body.description).to.equal(apiKeyUpdateBody.description); + + // API Key assertions are intentionally excluded to avoid being exposed in the logs + if (base_url.includes("sandbox") || base_url.includes("integ")) { + expect(response.body).to.have.property("key_id").and.to.include("snd_") + .and.to.not.be.empty; + } else if (base_url.includes("localhost")) { + expect(response.body).to.have.property("key_id").and.to.include("dev_") + .and.to.not.be.empty; + } + + if (api_key === undefined || api_key === null) { + globalState.set("apiKey", response.body.api_key); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `API Key create call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); + +// Routing API calls +Cypress.Commands.add( + "routingSetupCall", + (routingSetupBody, type, payload, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/routing_algorithm`; + + // Update request body + routingSetupBody.algorithm.data = payload.data; + routingSetupBody.algorithm.type = type; + routingSetupBody.description = payload.description; + routingSetupBody.name = payload.name; + routingSetupBody.profile_id = profile_id; + + cy.request({ + method: "POST", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + body: routingSetupBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.have.property("id").and.to.include("routing_"); + expect(response.body).to.have.property("kind").and.to.equal(type); + expect(response.body) + .to.have.property("profile_id") + .and.to.equal(profile_id); + + globalState.set("routingAlgorithmId", response.body.id); + } else { + // to be updated + throw new Error( + `Routing algorithm setup call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); +Cypress.Commands.add( + "routingActivateCall", + (routingActivationBody, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const routing_algorithm_id = globalState.get("routingAlgorithmId"); + const url = `${base_url}/v2/profiles/${profile_id}/activate_routing_algorithm`; + + // Update request body + routingActivationBody.routing_algorithm_id = routing_algorithm_id; + + cy.request({ + method: "PATCH", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + body: routingActivationBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.have.property("id").and.to.include("routing_"); + expect(response.body) + .to.have.property("profile_id") + .and.to.equal(profile_id); + } else { + // to be updated + throw new Error( + `Routing algorithm activation call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); +Cypress.Commands.add("routingActivationRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const query_params = "limit=10"; + const routing_algorithm_id = globalState.get("routingAlgorithmId"); + const url = `${base_url}/v2/profiles/${profile_id}/routing_algorithm?${query_params}`; + + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.be.an("array").and.to.not.be.empty; + for (const key in response.body) { + expect(response.body[key]) + .to.have.property("id") + .and.to.include("routing_"); + expect(response.body[key]) + .to.have.property("profile_id") + .and.to.equal(profile_id); + } + } else { + // to be updated + throw new Error( + `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("routingDeactivateCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const routing_algorithm_id = globalState.get("routingAlgorithmId"); + const url = `${base_url}/v2/profiles/${profile_id}/deactivate_routing_algorithm`; + + cy.request({ + method: "PATCH", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body) + .to.have.property("id") + .and.to.include("routing_") + .and.to.equal(routing_algorithm_id); + expect(response.body) + .to.have.property("profile_id") + .and.to.equal(profile_id); + } else { + // to be updated + throw new Error( + `Routing algorithm deactivation call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("routingRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const routing_algorithm_id = globalState.get("routingAlgorithmId"); + const url = `${base_url}/v2/routing_algorithm/${routing_algorithm_id}`; + + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body) + .to.have.property("id") + .and.to.include("routing_") + .and.to.equal(routing_algorithm_id); + expect(response.body) + .to.have.property("profile_id") + .and.to.equal(profile_id); + expect(response.body).to.have.property("algorithm").and.to.be.a("object") + .and.not.be.empty; + } else { + // to be updated + throw new Error( + `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add( + "routingDefaultFallbackCall", + (routingDefaultFallbackBody, payload, globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const routing_algorithm_id = globalState.get("routingAlgorithmId"); + const url = `${base_url}/v2/profiles/${profile_id}/fallback_routing`; + + // Update request body + routingDefaultFallbackBody = payload; + + cy.request({ + method: "POST", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + body: routingDefaultFallbackBody, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.deep.equal(routingDefaultFallbackBody); + } else { + // to be updated + throw new Error( + `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); + } +); +Cypress.Commands.add("routingFallbackRetrieveCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("userInfoToken"); + const base_url = globalState.get("baseUrl"); + const profile_id = globalState.get("profileId"); + const url = `${base_url}/v2/profiles/${profile_id}/fallback_routing`; + + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.be.an("array").and.to.not.be.empty; + } else { + // to be updated + throw new Error( + `Routing algorithm activation retrieve call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); + +// User API calls +// Below 3 commands should be called in sequence to login a user +Cypress.Commands.add("userLogin", (globalState) => { + // Define the necessary variables and constant + const base_url = globalState.get("baseUrl"); + const query_params = `token_only=true`; + const signin_body = { + email: `${globalState.get("email")}`, + password: `${globalState.get("password")}`, + }; + const url = `${base_url}/user/v2/signin?${query_params}`; + + cy.request({ + method: "POST", + url: url, + headers: { + "Content-Type": "application/json", + }, + body: signin_body, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + if (response.body.token_type === "totp") { + expect(response.body).to.have.property("token").and.to.not.be.empty; + + globalState.set("totpToken", response.body.token); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `User login call failed to get totp token with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("terminate2Fa", (globalState) => { + // Define the necessary variables and constant + const base_url = globalState.get("baseUrl"); + const query_params = `skip_two_factor_auth=true`; + const api_key = globalState.get("totpToken"); + const url = `${base_url}/user/2fa/terminate?${query_params}`; + + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + if (response.body.token_type === "user_info") { + expect(response.body).to.have.property("token").and.to.not.be.empty; + + globalState.set("userInfoToken", response.body.token); + cy.task("setGlobalState", globalState.data); + } + } else { + // to be updated + throw new Error( + `2FA terminate call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("userInfo", (globalState) => { + // Define the necessary variables and constant + const base_url = globalState.get("baseUrl"); + const api_key = globalState.get("userInfoToken"); + const url = `${base_url}/user`; + + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.have.property("merchant_id").and.to.not.be.empty; + expect(response.body).to.have.property("org_id").and.to.not.be.empty; + expect(response.body).to.have.property("profile_id").and.to.not.be.empty; + + globalState.set("merchantId", response.body.merchant_id); + globalState.set("organizationId", response.body.org_id); + globalState.set("profileId", response.body.profile_id); + } else { + // to be updated + throw new Error( + `User login call failed to fetch user info with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); + +// List API calls +Cypress.Commands.add("listMcaCall", (globalState) => { + // Define the necessary variables and constants + const api_key = globalState.get("adminApiKey"); + const base_url = globalState.get("baseUrl"); + const merchant_id = globalState.get("merchantId"); + const url = `${base_url}/v2/account/${merchant_id}/connectors`; + + const customHeaders = { + "x-merchant-id": merchant_id, + }; + + cy.request({ + method: "GET", + url: url, + headers: { + "api-key": api_key, + "Content-Type": "application/json", + ...customHeaders, + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + // Control never comes here until the API endpoints are introduced + console.log("List MCA call successful"); + globalState.set( + "adyenMerchantConnectorId", + response.body[2].merchant_connector_id + ); + globalState.set( + "bluesnapMerchantConnectorId", + response.body[0].merchant_connector_id + ); + globalState.set( + "stripeMerchantConnectorId", + response.body[1].merchant_connector_id + ); + } else if (response.status === 404) { + expect(response.body.error) + .to.have.property("message") + .and.to.equal("Unrecognized request URL"); + expect(response.body.error) + .to.have.property("type") + .and.to.equal("invalid_request"); + + // hard code MCA values for now + if (base_url.includes("integ")) { + globalState.set("adyenMerchantConnectorId", "mca_YOGOW6CdrjudsT9Mvg7w"); + globalState.set( + "bluesnapMerchantConnectorId", + "mca_cdKJoouwpmkHqwVJ1bzV" + ); + globalState.set( + "stripeMerchantConnectorId", + "mca_KyxoOnfLXWE1hzPSsl9H" + ); + } + } else { + // to be updated + throw new Error( + `MCA list call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +// templates +Cypress.Commands.add("", () => { + cy.request({}).then((response) => {}); +}); +Cypress.Commands.add("", () => { + cy.request({}).then((response) => {}); +}); +Cypress.Commands.add("", () => { + cy.request({}).then((response) => {}); +}); diff --git a/cypress-tests-v2/cypress/support/e2e.js b/cypress-tests-v2/cypress/support/e2e.js new file mode 100644 index 00000000000..a3e0203027c --- /dev/null +++ b/cypress-tests-v2/cypress/support/e2e.js @@ -0,0 +1,21 @@ +// *********************************************************** +// This example support/e2e.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import "cypress-mochawesome-reporter/register"; +import "./commands"; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/cypress-tests-v2/cypress/support/redirectionHandler.js b/cypress-tests-v2/cypress/support/redirectionHandler.js new file mode 100644 index 00000000000..6540632c893 --- /dev/null +++ b/cypress-tests-v2/cypress/support/redirectionHandler.js @@ -0,0 +1,476 @@ +import jsQR from "jsqr"; + +// Define constants for wait times +const TIMEOUT = 20000; // 20 seconds +const WAIT_TIME = 10000; // 10 seconds +const WAIT_TIME_IATAPAY = 20000; // 20 seconds + +export function handleRedirection( + redirection_type, + urls, + connectorId, + payment_method_type, + handler_metadata +) { + switch (redirection_type) { + case "bank_redirect": + bankRedirectRedirection( + urls.redirection_url, + urls.expected_url, + connectorId, + payment_method_type + ); + break; + case "bank_transfer": + bankTransferRedirection( + urls.redirection_url, + urls.expected_url, + connectorId, + payment_method_type, + handler_metadata.next_action_type + ); + break; + case "three_ds": + threeDsRedirection(urls.redirection_url, urls.expected_url, connectorId); + break; + case "upi": + upiRedirection( + urls.redirection_url, + urls.expected_url, + connectorId, + payment_method_type + ); + break; + default: + throw new Error(`Redirection known: ${redirection_type}`); + } +} + +function bankTransferRedirection( + redirection_url, + expected_url, + connectorId, + payment_method_type, + next_action_type +) { + switch (next_action_type) { + case "qr_code_url": + cy.request(redirection_url.href).then((response) => { + switch (connectorId) { + case "adyen": + switch (payment_method_type) { + case "pix": + expect(response.status).to.eq(200); + fetchAndParseQRCode(redirection_url.href).then((qrCodeData) => { + expect(qrCodeData).to.eq("TestQRCodeEMVToken"); + }); + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + // expected_redirection can be used here to handle other payment methods + } + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + } + }); + break; + case "image_data_url": + switch (connectorId) { + case "itaubank": + switch (payment_method_type) { + case "pix": + fetchAndParseImageData(redirection_url).then((qrCodeData) => { + expect(qrCodeData).to.contains("itau.com.br/pix/qr/v2"); // image data contains the following value + }); + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + } + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + } + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + } +} + +function bankRedirectRedirection( + redirection_url, + expected_url, + connectorId, + payment_method_type +) { + let verifyUrl = false; + cy.visit(redirection_url.href); + + switch (connectorId) { + case "adyen": + switch (payment_method_type) { + case "eps": + cy.get("h1").should("contain.text", "Acquirer Simulator"); + cy.get('[value="authorised"]').click(); + cy.url().should("include", "status=succeeded"); + cy.wait(5000); + break; + case "ideal": + cy.get(":nth-child(4) > td > p").should( + "contain.text", + "Your Payment was Authorised/Refused/Cancelled (It may take up to five minutes to show on the Payment List)" + ); + cy.get(".btnLink").click(); + cy.url().should("include", "status=succeeded"); + cy.wait(5000); + break; + case "giropay": + cy.get( + ".rds-cookies-overlay__allow-all-cookies-btn > .rds-button" + ).click(); + cy.wait(5000); + cy.get(".normal-3").should( + "contain.text", + "Bank suchen ‑ mit giropay zahlen." + ); + cy.get("#bankSearch").type("giropay TestBank{enter}"); + cy.get(".normal-2 > div").click(); + cy.get('[data-testid="customerIban"]').type("DE48499999601234567890"); + cy.get('[data-testid="customerIdentification"]').type("9123456789"); + cy.get(":nth-child(3) > .rds-button").click(); + cy.get('[data-testid="onlineBankingPin"]').type("1234"); + cy.get(".rds-button--primary").click(); + cy.get(":nth-child(5) > .rds-radio-input-group__label").click(); + cy.get(".rds-button--primary").click(); + cy.get('[data-testid="photoTan"]').type("123456"); + cy.get(".rds-button--primary").click(); + cy.wait(5000); + cy.url().should("include", "status=succeeded"); + cy.wait(5000); + break; + case "sofort": + cy.get(".modal-overlay.modal-shown.in", { timeout: TIMEOUT }).then( + ($modal) => { + // If modal is found, handle it + if ($modal.length > 0) { + cy.get("button.cookie-modal-deny-all.button-tertiary") + .should("be.visible") + .should("contain", "Reject All") + .click({ force: true, multiple: true }); + cy.get("div#TopBanks.top-banks-multistep") + .should("contain", "Demo Bank") + .as("btn") + .click(); + cy.get("@btn").click(); + } else { + cy.get("input.phone").type("9123456789"); + cy.get("#button.onContinue") + .should("contain", "Continue") + .click(); + } + } + ); + break; + case "trustly": + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + verifyUrl = true; + break; + case "paypal": + switch (payment_method_type) { + case "eps": + cy.get('button[name="Successful"][value="SUCCEEDED"]').click(); + break; + case "ideal": + cy.get('button[name="Successful"][value="SUCCEEDED"]').click(); + break; + case "giropay": + cy.get('button[name="Successful"][value="SUCCEEDED"]').click(); + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + verifyUrl = true; + break; + case "stripe": + switch (payment_method_type) { + case "eps": + cy.get('a[name="success"]').click(); + break; + case "ideal": + cy.get('a[name="success"]').click(); + break; + case "giropay": + cy.get('a[name="success"]').click(); + break; + case "sofort": + cy.get('a[name="success"]').click(); + break; + case "przelewy24": + cy.get('a[name="success"]').click(); + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + verifyUrl = true; + break; + case "trustpay": + switch (payment_method_type) { + case "eps": + cy.get("._transactionId__header__iXVd_").should( + "contain.text", + "Bank suchen ‑ mit eps zahlen." + ); + cy.get(".BankSearch_searchInput__uX_9l").type( + "Allgemeine Sparkasse Oberösterreich Bank AG{enter}" + ); + cy.get(".BankSearch_searchResultItem__lbcKm").click(); + cy.get("._transactionId__primaryButton__nCa0r").click(); + cy.get("#loginTitle").should( + "contain.text", + "eps Online-Überweisung Login" + ); + cy.get("#user") + .should("be.visible") + .should("be.enabled") + .focus() + .type("Verfügernummer"); + cy.get("input#submitButton.btn.btn-primary").click(); + break; + case "ideal": + cy.contains("button", "Select your bank").click(); + cy.get( + 'button[data-testid="bank-item"][id="bank-item-INGBNL2A"]' + ).click(); + break; + case "giropay": + cy.get("._transactionId__header__iXVd_").should( + "contain.text", + "Bank suchen ‑ mit giropay zahlen." + ); + cy.get(".BankSearch_searchInput__uX_9l").type( + "Volksbank Hildesheim{enter}" + ); + cy.get(".BankSearch_searchIcon__EcVO7").click(); + cy.get(".BankSearch_bankWrapper__R5fUK").click(); + cy.get("._transactionId__primaryButton__nCa0r").click(); + cy.get(".normal-3").should("contain.text", "Kontoauswahl"); + break; + case "sofort": + break; + case "trustly": + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + verifyUrl = false; + break; + default: + throw new Error(`Unsupported connector: ${connectorId}`); + } + + cy.then(() => { + verifyReturnUrl(redirection_url, expected_url, verifyUrl); + }); +} + +function threeDsRedirection(redirection_url, expected_url, connectorId) { + cy.visit(redirection_url.href); + if (connectorId === "adyen") { + cy.get("iframe") + .its("0.contentDocument.body") + .within((body) => { + cy.get('input[type="password"]').click(); + cy.get('input[type="password"]').type("password"); + cy.get("#buttonSubmit").click(); + }); + } else if ( + connectorId === "bankofamerica" || + connectorId === "cybersource" || + connectorId === "wellsfargo" + ) { + cy.get("iframe", { timeout: TIMEOUT }) + .its("0.contentDocument.body") + .within((body) => { + cy.get('input[type="text"]').click().type("1234"); + cy.get('input[value="SUBMIT"]').click(); + }); + } else if (connectorId === "nmi" || connectorId === "noon") { + cy.get("iframe", { timeout: TIMEOUT }) + .its("0.contentDocument.body") + .within((body) => { + cy.get("iframe", { timeout: TIMEOUT }) + .its("0.contentDocument.body") + .within((body) => { + cy.get('form[name="cardholderInput"]', { timeout: TIMEOUT }) + .should("exist") + .then((form) => { + cy.get('input[name="challengeDataEntry"]').click().type("1234"); + cy.get('input[value="SUBMIT"]').click(); + }); + }); + }); + } else if (connectorId === "stripe") { + cy.get("iframe", { timeout: TIMEOUT }) + .its("0.contentDocument.body") + .within((body) => { + cy.get("iframe") + .its("0.contentDocument.body") + .within((body) => { + cy.get("#test-source-authorize-3ds").click(); + }); + }); + } else if (connectorId === "trustpay") { + cy.get('form[name="challengeForm"]', { timeout: WAIT_TIME }) + .should("exist") + .then((form) => { + cy.get("#outcomeSelect").select("Approve").should("have.value", "Y"); + cy.get('button[type="submit"]').click(); + }); + } else { + // If connectorId is neither of adyen, trustpay, nmi, stripe, bankofamerica or cybersource, wait for 10 seconds + cy.wait(WAIT_TIME); + } + + cy.then(() => { + verifyReturnUrl(redirection_url, expected_url, true); + }); +} + +function upiRedirection( + redirection_url, + expected_url, + connectorId, + payment_method_type +) { + let verifyUrl = false; + if (connectorId === "iatapay") { + switch (payment_method_type) { + case "upi_collect": + cy.visit(redirection_url.href); + cy.wait(WAIT_TIME_IATAPAY).then(() => { + verifyUrl = true; + }); + break; + case "upi_intent": + cy.request(redirection_url.href).then((response) => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property("iataPaymentId"); + expect(response.body).to.have.property("status", "INITIATED"); + expect(response.body.qrInfoData).to.be.an("object"); + expect(response.body.qrInfoData).to.have.property("qr"); + expect(response.body.qrInfoData).to.have.property("qrLink"); + }); + verifyUrl = false; + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + } else { + // If connectorId is not iatapay, wait for 10 seconds + cy.wait(WAIT_TIME); + } + + cy.then(() => { + verifyReturnUrl(redirection_url, expected_url, verifyUrl); + }); +} + +function verifyReturnUrl(redirection_url, expected_url, forward_flow) { + if (forward_flow) { + // Handling redirection + if (redirection_url.host.endsWith(expected_url.host)) { + // No CORS workaround needed + cy.window().its("location.origin").should("eq", expected_url.origin); + } else { + // Workaround for CORS to allow cross-origin iframe + cy.origin( + expected_url.origin, + { args: { expected_url: expected_url.origin } }, + ({ expected_url }) => { + cy.window().its("location.origin").should("eq", expected_url); + } + ); + } + } +} + +async function fetchAndParseQRCode(url) { + const response = await fetch(url, { encoding: "binary" }); + if (!response.ok) { + throw new Error(`Failed to fetch QR code image: ${response.statusText}`); + } + const blob = await response.blob(); + const reader = new FileReader(); + return await new Promise((resolve, reject) => { + reader.onload = () => { + const base64Image = reader.result.split(",")[1]; // Remove data URI prefix + const image = new Image(); + image.src = base64Image; + + image.onload = () => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + canvas.width = image.width; + canvas.height = image.height; + ctx.drawImage(image, 0, 0); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const qrCodeData = jsQR( + imageData.data, + imageData.width, + imageData.height + ); + + if (qrCodeData) { + resolve(qrCodeData.data); + } else { + reject(new Error("Failed to decode QR code")); + } + }; + image.onerror = reject; // Handle image loading errors + }; + reader.readAsDataURL(blob); + }); +} + +async function fetchAndParseImageData(url) { + return await new Promise((resolve, reject) => { + const image = new Image(); + image.src = url; + + image.onload = () => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + canvas.width = image.width; + canvas.height = image.height; + ctx.drawImage(image, 0, 0); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const qrCodeData = jsQR( + imageData.data, + imageData.width, + imageData.height + ); + + if (qrCodeData) { + resolve(qrCodeData.data); + } else { + reject(new Error("Failed to decode QR code")); + } + }; + image.onerror = reject; // Handle image loading errors + }); +} diff --git a/cypress-tests-v2/cypress/utils/State.js b/cypress-tests-v2/cypress/utils/State.js new file mode 100644 index 00000000000..84f96c0bda8 --- /dev/null +++ b/cypress-tests-v2/cypress/utils/State.js @@ -0,0 +1,24 @@ +class State { + data = {}; + constructor(data) { + this.data = data; + this.data["connectorId"] = Cypress.env("CONNECTOR"); + this.data["baseUrl"] = Cypress.env("BASEURL"); + this.data["adminApiKey"] = Cypress.env("ADMINAPIKEY"); + this.data["email"] = Cypress.env("HS_EMAIL"); + this.data["password"] = Cypress.env("HS_PASSWORD"); + this.data["connectorAuthFilePath"] = Cypress.env( + "CONNECTOR_AUTH_FILE_PATH" + ); + } + + set(key, val) { + this.data[key] = val; + } + + get(key) { + return this.data[key]; + } +} + +export default State; diff --git a/cypress-tests-v2/package-lock.json b/cypress-tests-v2/package-lock.json new file mode 100644 index 00000000000..d9282b8d3ce --- /dev/null +++ b/cypress-tests-v2/package-lock.json @@ -0,0 +1,3456 @@ +{ + "name": "cypress", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cypress", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "cypress": "^13.14.2", + "cypress-mochawesome-reporter": "^3.8.2", + "jsqr": "^1.4.0", + "prettier": "^3.3.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cypress/request": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", + "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "6.13.0", + "safe-buffer": "^5.1.2", + "tough-cookie": "^4.1.3", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "node_modules/@cypress/xvfb/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sizzle": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", + "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/cachedir": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cypress": { + "version": "13.14.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.2.tgz", + "integrity": "sha512-lsiQrN17vHMB2fnvxIrKLAjOr9bPwsNbPZNrWf99s4u+DVmCY6U+w7O3GGG9FvP4EUVYaDu+guWeNLiUzBrqvA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@cypress/request": "^3.0.1", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.7.1", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-ci": "^3.0.1", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "process": "^0.11.10", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.5.3", + "supports-color": "^8.1.1", + "tmp": "~0.2.3", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + } + }, + "node_modules/cypress-mochawesome-reporter": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.8.2.tgz", + "integrity": "sha512-oJZkNzhNmN9ZD+LmZyFuPb8aWaIijyHyqYh52YOBvR6B6ckfJNCHP3A98a+/nG0H4t46CKTNwo+wNpMa4d2kjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.1", + "fs-extra": "^10.0.1", + "mochawesome": "^7.1.3", + "mochawesome-merge": "^4.2.1", + "mochawesome-report-generator": "^6.2.0" + }, + "bin": { + "generate-mochawesome-report": "cli.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/LironEr" + }, + "peerDependencies": { + "cypress": ">=6.2.0" + } + }, + "node_modules/cypress-mochawesome-reporter/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/cypress-mochawesome-reporter/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fsu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fsu/-/fsu-1.1.1.tgz", + "integrity": "sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==", + "dev": true, + "license": "MIT" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "node_modules/jsqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz", + "integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "> 0.8" + } + }, + "node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mocha": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mochawesome": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/mochawesome/-/mochawesome-7.1.3.tgz", + "integrity": "sha512-Vkb3jR5GZ1cXohMQQ73H3cZz7RoxGjjUo0G5hu0jLaW+0FdUxUwg3Cj29bqQdh0rFcnyV06pWmqmi5eBPnEuNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "diff": "^5.0.0", + "json-stringify-safe": "^5.0.1", + "lodash.isempty": "^4.4.0", + "lodash.isfunction": "^3.0.9", + "lodash.isobject": "^3.0.2", + "lodash.isstring": "^4.0.1", + "mochawesome-report-generator": "^6.2.0", + "strip-ansi": "^6.0.1", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "mocha": ">=7" + } + }, + "node_modules/mochawesome-merge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mochawesome-merge/-/mochawesome-merge-4.3.0.tgz", + "integrity": "sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^7.0.1", + "glob": "^7.1.6", + "yargs": "^15.3.1" + }, + "bin": { + "mochawesome-merge": "bin/mochawesome-merge.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mochawesome-merge/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/mochawesome-merge/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-merge/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/mochawesome-merge/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mochawesome-merge/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mochawesome-merge/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/mochawesome-merge/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mochawesome-merge/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/mochawesome-merge/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mochawesome-merge/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mochawesome-merge/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mochawesome-merge/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mochawesome-merge/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mochawesome-merge/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mochawesome-merge/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/mochawesome-merge/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mochawesome-merge/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mochawesome-report-generator": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mochawesome-report-generator/-/mochawesome-report-generator-6.2.0.tgz", + "integrity": "sha512-Ghw8JhQFizF0Vjbtp9B0i//+BOkV5OWcQCPpbO0NGOoxV33o+gKDYU0Pr2pGxkIHnqZ+g5mYiXF7GMNgAcDpSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "dateformat": "^4.5.1", + "escape-html": "^1.0.3", + "fs-extra": "^10.0.0", + "fsu": "^1.1.1", + "lodash.isfunction": "^3.0.9", + "opener": "^1.5.2", + "prop-types": "^15.7.2", + "tcomb": "^3.2.17", + "tcomb-validation": "^3.3.0", + "validator": "^13.6.0", + "yargs": "^17.2.1" + }, + "bin": { + "marge": "bin/cli.js" + } + }, + "node_modules/mochawesome-report-generator/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mochawesome-report-generator/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mochawesome-report-generator/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mochawesome-report-generator/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "throttleit": "^1.0.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tcomb": { + "version": "3.2.29", + "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz", + "integrity": "sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tcomb-validation": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tcomb-validation/-/tcomb-validation-3.4.1.tgz", + "integrity": "sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tcomb": "^3.0.0" + } + }, + "node_modules/throttleit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", + "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/cypress-tests-v2/package.json b/cypress-tests-v2/package.json new file mode 100644 index 00000000000..e06c1a63be2 --- /dev/null +++ b/cypress-tests-v2/package.json @@ -0,0 +1,23 @@ +{ + "name": "cypress", + "version": "1.0.0", + "description": "Cypress tests suite", + "scripts": { + "cypress": "npx cypress open", + "cypress-e2e": "npx cypress run --e2e", + "cypress:ci": "npx cypress run --headless", + "cypress:payments": "cypress run --headless --spec 'cypress/e2e/spec/Payment/**/*'", + "cypress:payouts": "cypress run --headless --spec 'cypress/e2e/spec/Payout/**/*'", + "cypress:payment-method-list": "cypress run --headless --spec 'cypress/e2e/spec/PaymentMethodList/**/*'", + "cypress:routing": "cypress run --headless --spec 'cypress/e2e/spec/Routing/**/*'" + }, + "author": "Hyperswitch Team", + "license": "ISC", + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "cypress": "^13.14.2", + "cypress-mochawesome-reporter": "^3.8.2", + "jsqr": "^1.4.0", + "prettier": "^3.3.2" + } +} diff --git a/cypress-tests/.prettierrc.json b/cypress-tests/.prettierrc.json index f0eb61e0f7c..479993c1334 100644 --- a/cypress-tests/.prettierrc.json +++ b/cypress-tests/.prettierrc.json @@ -2,5 +2,6 @@ "trailingComma": "es5", "tabWidth": 2, "semi": true, - "singleQuote": false + "singleQuote": false, + "printWidth": 120 } From 8320dc07fe1b8b1c9427f70dcb9d952eef01a63b Mon Sep 17 00:00:00 2001 From: Kashif Date: Thu, 19 Sep 2024 18:54:56 +0530 Subject: [PATCH 24/48] feat(payments): store and propagate additional wallet pm details in payments response (#5869) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 65 ++++++++++++++++++- api-reference/openapi_spec.json | 65 ++++++++++++++++++- crates/api_models/src/payments.rs | 48 +++++++++++++- .../src/payments/additional_info.rs | 12 +++- crates/openapi/src/openapi.rs | 3 + crates/openapi/src/openapi_v2.rs | 3 + crates/router/src/core/payments/helpers.rs | 16 ++++- 7 files changed, 205 insertions(+), 7 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 04c72b1e8a7..558a3198281 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -11912,7 +11912,7 @@ ], "properties": { "wallet": { - "type": "object" + "$ref": "#/components/schemas/WalletResponse" } } }, @@ -19124,6 +19124,28 @@ } ] }, + "WalletAdditionalDataForCard": { + "type": "object", + "required": [ + "last4", + "card_network", + "type" + ], + "properties": { + "last4": { + "type": "string", + "description": "Last 4 digits of the card number" + }, + "card_network": { + "type": "string", + "description": "The information of the payment method" + }, + "type": { + "type": "string", + "description": "The type of payment method" + } + } + }, "WalletData": { "oneOf": [ { @@ -19428,6 +19450,47 @@ } ] }, + "WalletResponse": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/WalletResponseData" + } + ], + "nullable": true + }, + { + "type": "object" + } + ] + }, + "WalletResponseData": { + "oneOf": [ + { + "type": "object", + "required": [ + "apple_pay" + ], + "properties": { + "apple_pay": { + "$ref": "#/components/schemas/WalletAdditionalDataForCard" + } + } + }, + { + "type": "object", + "required": [ + "google_pay" + ], + "properties": { + "google_pay": { + "$ref": "#/components/schemas/WalletAdditionalDataForCard" + } + } + } + ] + }, "WeChatPay": { "type": "object" }, diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 7ef33c1c945..8214d3a10be 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -15726,7 +15726,7 @@ ], "properties": { "wallet": { - "type": "object" + "$ref": "#/components/schemas/WalletResponse" } } }, @@ -23465,6 +23465,28 @@ } ] }, + "WalletAdditionalDataForCard": { + "type": "object", + "required": [ + "last4", + "card_network", + "type" + ], + "properties": { + "last4": { + "type": "string", + "description": "Last 4 digits of the card number" + }, + "card_network": { + "type": "string", + "description": "The information of the payment method" + }, + "type": { + "type": "string", + "description": "The type of payment method" + } + } + }, "WalletData": { "oneOf": [ { @@ -23769,6 +23791,47 @@ } ] }, + "WalletResponse": { + "allOf": [ + { + "allOf": [ + { + "$ref": "#/components/schemas/WalletResponseData" + } + ], + "nullable": true + }, + { + "type": "object" + } + ] + }, + "WalletResponseData": { + "oneOf": [ + { + "type": "object", + "required": [ + "apple_pay" + ], + "properties": { + "apple_pay": { + "$ref": "#/components/schemas/WalletAdditionalDataForCard" + } + } + }, + { + "type": "object", + "required": [ + "google_pay" + ], + "properties": { + "google_pay": { + "$ref": "#/components/schemas/WalletAdditionalDataForCard" + } + } + } + ] + }, "WeChatPay": { "type": "object" }, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 20f1205e14b..715bf3c3544 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2046,6 +2046,7 @@ pub enum AdditionalPaymentData { }, Wallet { apple_pay: Option, + google_pay: Option, }, PayLater { klarna_sdk: Option, @@ -3069,7 +3070,7 @@ where | PaymentMethodDataResponse::PayLater(_) | PaymentMethodDataResponse::RealTimePayment(_) | PaymentMethodDataResponse::Upi(_) - | PaymentMethodDataResponse::Wallet {} + | PaymentMethodDataResponse::Wallet(_) | PaymentMethodDataResponse::BankTransfer(_) | PaymentMethodDataResponse::OpenBanking(_) | PaymentMethodDataResponse::Voucher(_) => { @@ -3090,7 +3091,7 @@ where pub enum PaymentMethodDataResponse { Card(Box), BankTransfer(Box), - Wallet {}, + Wallet(Box), PayLater(Box), BankRedirect(Box), Crypto(Box), @@ -3187,6 +3188,21 @@ pub struct PaylaterResponse { klarna_sdk: Option, } +#[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, ToSchema)] +pub struct WalletResponse { + #[serde(flatten)] + details: Option, +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum WalletResponseData { + #[schema(value_type = WalletAdditionalDataForCard)] + ApplePay(Box), + #[schema(value_type = WalletAdditionalDataForCard)] + GooglePay(Box), +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] pub struct KlarnaSdkPaymentMethodResponse { @@ -4422,7 +4438,33 @@ impl From for PaymentMethodDataResponse { Some(sdk) => Self::PayLater(Box::new(PaylaterResponse::from(sdk))), None => Self::PayLater(Box::new(PaylaterResponse { klarna_sdk: None })), }, - AdditionalPaymentData::Wallet { .. } => Self::Wallet {}, + AdditionalPaymentData::Wallet { + apple_pay, + google_pay, + } => match (apple_pay, google_pay) { + (Some(apple_pay_pm), _) => Self::Wallet(Box::new(WalletResponse { + details: Some(WalletResponseData::ApplePay(Box::new( + additional_info::WalletAdditionalDataForCard { + last4: apple_pay_pm + .display_name + .clone() + .chars() + .rev() + .take(4) + .collect::() + .chars() + .rev() + .collect::(), + card_network: apple_pay_pm.network.clone(), + card_type: apple_pay_pm.pm_type.clone(), + }, + ))), + })), + (_, Some(google_pay_pm)) => Self::Wallet(Box::new(WalletResponse { + details: Some(WalletResponseData::GooglePay(Box::new(google_pay_pm))), + })), + _ => Self::Wallet(Box::new(WalletResponse { details: None })), + }, AdditionalPaymentData::BankRedirect { bank_name, details } => { Self::BankRedirect(Box::new(BankRedirectResponse { bank_name, details })) } diff --git a/crates/api_models/src/payments/additional_info.rs b/crates/api_models/src/payments/additional_info.rs index 77500b0edc5..9e8c910cba7 100644 --- a/crates/api_models/src/payments/additional_info.rs +++ b/crates/api_models/src/payments/additional_info.rs @@ -205,9 +205,19 @@ pub enum UpiAdditionalData { } #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] -#[serde(rename_all = "snake_case")] pub struct UpiCollectAdditionalData { /// Masked VPA ID #[schema(value_type = Option, example = "ab********@okhdfcbank")] pub vpa_id: Option, } + +#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] +pub struct WalletAdditionalDataForCard { + /// Last 4 digits of the card number + pub last4: String, + /// The information of the payment method + pub card_network: String, + /// The type of payment method + #[serde(rename = "type")] + pub card_type: String, +} diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index d71757e8f34..b626b7d4418 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -613,7 +613,10 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::additional_info::GivexGiftCardAdditionalData, api_models::payments::additional_info::UpiAdditionalData, api_models::payments::additional_info::UpiCollectAdditionalData, + api_models::payments::additional_info::WalletAdditionalDataForCard, api_models::payments::PaymentsDynamicTaxCalculationRequest, + api_models::payments::WalletResponse, + api_models::payments::WalletResponseData, api_models::payments::PaymentsDynamicTaxCalculationResponse, api_models::payments::DisplayAmountOnSdk, )), diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 8a21110ae83..f12d6a7b567 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -530,6 +530,9 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::additional_info::GivexGiftCardAdditionalData, api_models::payments::additional_info::UpiAdditionalData, api_models::payments::additional_info::UpiCollectAdditionalData, + api_models::payments::additional_info::WalletAdditionalDataForCard, + api_models::payments::WalletResponse, + api_models::payments::WalletResponseData, api_models::payments::PaymentsDynamicTaxCalculationRequest, api_models::payments::PaymentsDynamicTaxCalculationResponse, api_models::payments::DisplayAmountOnSdk, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index ce92c1ab007..81d9130db85 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -4261,9 +4261,23 @@ pub async fn get_additional_payment_data( network: apple_pay_wallet_data.payment_method.network.clone(), pm_type: apple_pay_wallet_data.payment_method.pm_type.clone(), }), + google_pay: None, }) } - _ => Some(api_models::payments::AdditionalPaymentData::Wallet { apple_pay: None }), + domain::WalletData::GooglePay(google_pay_pm_data) => { + Some(api_models::payments::AdditionalPaymentData::Wallet { + apple_pay: None, + google_pay: Some(payment_additional_types::WalletAdditionalDataForCard { + last4: google_pay_pm_data.info.card_details.clone(), + card_network: google_pay_pm_data.info.card_network.clone(), + card_type: google_pay_pm_data.pm_type.clone(), + }), + }) + } + _ => Some(api_models::payments::AdditionalPaymentData::Wallet { + apple_pay: None, + google_pay: None, + }), }, domain::PaymentMethodData::PayLater(_) => { Some(api_models::payments::AdditionalPaymentData::PayLater { klarna_sdk: None }) From a0f4bb771b583a8dad2a58158c64b7a8baff24d5 Mon Sep 17 00:00:00 2001 From: Kashif Date: Thu, 19 Sep 2024 18:55:12 +0530 Subject: [PATCH 25/48] feat(payout): add unified error code and messages along with translation (#5810) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 26 +- api-reference/openapi_spec.json | 26 +- crates/api_models/src/payouts.rs | 33 ++- crates/common_utils/src/consts.rs | 3 + crates/common_utils/src/types.rs | 106 ++++++++- crates/diesel_models/src/payout_attempt.rs | 19 ++ crates/diesel_models/src/schema.rs | 4 + crates/diesel_models/src/schema_v2.rs | 4 + .../src/payouts/payout_attempt.rs | 18 +- .../adyenplatform/transformers/payouts.rs | 10 +- .../generic_link/payout_link/status/script.js | 4 +- .../payout_link/status/styles.css | 3 +- crates/router/src/core/payout_link.rs | 19 +- crates/router/src/core/payouts.rs | 225 ++++++++++++++++-- crates/router/src/core/payouts/helpers.rs | 37 ++- crates/router/src/core/payouts/retry.rs | 13 +- .../router/src/core/payouts/transformers.rs | 4 +- crates/router/src/core/webhooks/incoming.rs | 16 +- crates/router/src/routes/payouts.rs | 60 ++++- .../attach_payout_account_workflow.rs | 16 +- .../src/workflows/outgoing_webhook_retry.rs | 19 +- .../src/payouts/payout_attempt.rs | 14 ++ .../down.sql | 3 + .../up.sql | 3 + 24 files changed, 605 insertions(+), 80 deletions(-) create mode 100644 migrations/2024-09-03-053218_add_unified_code_message_to_payout/down.sql create mode 100644 migrations/2024-09-03-053218_add_unified_code_message_to_payout/up.sql diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 558a3198281..5465d902388 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -15606,13 +15606,17 @@ }, "unified_code": { "type": "string", - "description": "error code unified across the connectors is received here if there was an error while calling connector", - "nullable": true + "description": "(This field is not live yet)\nError code unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "UE_000", + "nullable": true, + "maxLength": 255 }, "unified_message": { "type": "string", - "description": "error message unified across the connectors is received here if there was an error while calling connector", - "nullable": true + "description": "(This field is not live yet)\nError message unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "Invalid card details", + "nullable": true, + "maxLength": 1024 } } }, @@ -16126,6 +16130,20 @@ "example": "+1", "nullable": true, "maxLength": 255 + }, + "unified_code": { + "type": "string", + "description": "(This field is not live yet)\nError code unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "UE_000", + "nullable": true, + "maxLength": 255 + }, + "unified_message": { + "type": "string", + "description": "(This field is not live yet)\nError message unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "Invalid card details", + "nullable": true, + "maxLength": 1024 } }, "additionalProperties": false diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 8214d3a10be..ff1a93454cc 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -19417,13 +19417,17 @@ }, "unified_code": { "type": "string", - "description": "error code unified across the connectors is received here if there was an error while calling connector", - "nullable": true + "description": "(This field is not live yet)\nError code unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "UE_000", + "nullable": true, + "maxLength": 255 }, "unified_message": { "type": "string", - "description": "error message unified across the connectors is received here if there was an error while calling connector", - "nullable": true + "description": "(This field is not live yet)\nError message unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "Invalid card details", + "nullable": true, + "maxLength": 1024 } } }, @@ -19930,6 +19934,20 @@ "example": "+1", "nullable": true, "maxLength": 255 + }, + "unified_code": { + "type": "string", + "description": "(This field is not live yet)\nError code unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "UE_000", + "nullable": true, + "maxLength": 255 + }, + "unified_message": { + "type": "string", + "description": "(This field is not live yet)\nError message unified across the connectors is received here in case of errors while calling the underlying connector", + "example": "Invalid card details", + "nullable": true, + "maxLength": 1024 } }, "additionalProperties": false diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index 01bb63642be..10771df4ccd 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -5,6 +5,7 @@ use common_utils::{ consts::default_payouts_list_limit, crypto, id_type, link_utils, pii::{self, Email}, + types::{UnifiedCode, UnifiedMessage}, }; use masking::Secret; use router_derive::FlatStruct; @@ -383,7 +384,7 @@ pub struct Venmo { pub telephone_number: Option>, } -#[derive(Debug, ToSchema, Clone, Serialize)] +#[derive(Debug, ToSchema, Clone, Serialize, router_derive::PolymorphicSchema)] #[serde(deny_unknown_fields)] pub struct PayoutCreateResponse { /// Unique identifier for the payout. This ensures idempotency for multiple payouts @@ -535,6 +536,18 @@ pub struct PayoutCreateResponse { /// Customer's phone country code. _Deprecated: Use customer object instead._ #[schema(deprecated, max_length = 255, example = "+1")] pub phone_country_code: Option, + + /// (This field is not live yet) + /// Error code unified across the connectors is received here in case of errors while calling the underlying connector + #[remove_in(PayoutCreateResponse)] + #[schema(value_type = Option, max_length = 255, example = "UE_000")] + pub unified_code: Option, + + /// (This field is not live yet) + /// Error message unified across the connectors is received here in case of errors while calling the underlying connector + #[remove_in(PayoutCreateResponse)] + #[schema(value_type = Option, max_length = 1024, example = "Invalid card details")] + pub unified_message: Option, } #[derive( @@ -568,10 +581,16 @@ pub struct PayoutAttemptResponse { pub connector_transaction_id: Option, /// If the payout was cancelled the reason provided here pub cancellation_reason: Option, - /// error code unified across the connectors is received here if there was an error while calling connector - pub unified_code: Option, - /// error message unified across the connectors is received here if there was an error while calling connector - pub unified_message: Option, + /// (This field is not live yet) + /// Error code unified across the connectors is received here in case of errors while calling the underlying connector + #[remove_in(PayoutAttemptResponse)] + #[schema(value_type = Option, max_length = 255, example = "UE_000")] + pub unified_code: Option, + /// (This field is not live yet) + /// Error message unified across the connectors is received here in case of errors while calling the underlying connector + #[remove_in(PayoutAttemptResponse)] + #[schema(value_type = Option, max_length = 1024, example = "Invalid card details")] + pub unified_message: Option, } #[derive(Default, Debug, Clone, Deserialize, ToSchema)] @@ -819,8 +838,8 @@ pub struct PayoutLinkStatusDetails { pub session_expiry: PrimitiveDateTime, pub return_url: Option, pub status: api_enums::PayoutStatus, - pub error_code: Option, - pub error_message: Option, + pub error_code: Option, + pub error_message: Option, #[serde(flatten)] pub ui_config: link_utils::GenericLinkUiConfigFormData, pub test_mode: bool, diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 299e70dc4c9..70f00ad9b3f 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -146,3 +146,6 @@ pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin"; pub const ROLE_ID_INTERNAL_VIEW_ONLY_USER: &str = "internal_view_only"; /// Role ID for Internal Admin pub const ROLE_ID_INTERNAL_ADMIN: &str = "internal_admin"; + +/// Payout flow identifier used for performing GSM operations +pub const PAYOUT_FLOW_STR: &str = "payout_flow"; diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 7f2f844b03f..6acf5000ced 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -32,7 +32,7 @@ use utoipa::ToSchema; use crate::{ consts, - errors::{CustomResult, ParsingError, PercentageError}, + errors::{CustomResult, ParsingError, PercentageError, ValidationError}, }; /// Represents Percentage Value between 0 and 100 both inclusive #[derive(Clone, Default, Debug, PartialEq, Serialize)] @@ -767,3 +767,107 @@ where self.0.to_sql(out) } } + +/// Domain type for unified code +#[derive( + Debug, Clone, PartialEq, Eq, Queryable, serde::Deserialize, serde::Serialize, AsExpression, +)] +#[diesel(sql_type = sql_types::Text)] +pub struct UnifiedCode(pub String); + +impl TryFrom for UnifiedCode { + type Error = error_stack::Report; + fn try_from(src: String) -> Result { + if src.len() > 255 { + Err(report!(ValidationError::InvalidValue { + message: "unified_code's length should not exceed 255 characters".to_string() + })) + } else { + Ok(Self(src)) + } + } +} + +impl Queryable for UnifiedCode +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } +} +impl FromSql for UnifiedCode +where + DB: Backend, + String: FromSql, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { + let val = String::from_sql(bytes)?; + Ok(Self::try_from(val)?) + } +} + +impl ToSql for UnifiedCode +where + DB: Backend, + String: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +/// Domain type for unified messages +#[derive( + Debug, Clone, PartialEq, Eq, Queryable, serde::Deserialize, serde::Serialize, AsExpression, +)] +#[diesel(sql_type = sql_types::Text)] +pub struct UnifiedMessage(pub String); + +impl TryFrom for UnifiedMessage { + type Error = error_stack::Report; + fn try_from(src: String) -> Result { + if src.len() > 1024 { + Err(report!(ValidationError::InvalidValue { + message: "unified_message's length should not exceed 1024 characters".to_string() + })) + } else { + Ok(Self(src)) + } + } +} + +impl Queryable for UnifiedMessage +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } +} +impl FromSql for UnifiedMessage +where + DB: Backend, + String: FromSql, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { + let val = String::from_sql(bytes)?; + Ok(Self::try_from(val)?) + } +} + +impl ToSql for UnifiedMessage +where + DB: Backend, + String: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} diff --git a/crates/diesel_models/src/payout_attempt.rs b/crates/diesel_models/src/payout_attempt.rs index 619e694f411..edb60bc1f44 100644 --- a/crates/diesel_models/src/payout_attempt.rs +++ b/crates/diesel_models/src/payout_attempt.rs @@ -1,3 +1,4 @@ +use common_utils::types::{UnifiedCode, UnifiedMessage}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use serde::{self, Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -30,6 +31,8 @@ pub struct PayoutAttempt { pub profile_id: common_utils::id_type::ProfileId, pub merchant_connector_id: Option, pub routing_info: Option, + pub unified_code: Option, + pub unified_message: Option, } #[derive( @@ -66,6 +69,8 @@ pub struct PayoutAttemptNew { pub profile_id: common_utils::id_type::ProfileId, pub merchant_connector_id: Option, pub routing_info: Option, + pub unified_code: Option, + pub unified_message: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -76,6 +81,8 @@ pub enum PayoutAttemptUpdate { error_message: Option, error_code: Option, is_eligible: Option, + unified_code: Option, + unified_message: Option, }, PayoutTokenUpdate { payout_token: String, @@ -110,6 +117,8 @@ pub struct PayoutAttemptUpdateInternal { pub address_id: Option, pub customer_id: Option, pub merchant_connector_id: Option, + pub unified_code: Option, + pub unified_message: Option, } impl Default for PayoutAttemptUpdateInternal { @@ -129,6 +138,8 @@ impl Default for PayoutAttemptUpdateInternal { last_modified_at: common_utils::date_time::now(), address_id: None, customer_id: None, + unified_code: None, + unified_message: None, } } } @@ -146,12 +157,16 @@ impl From for PayoutAttemptUpdateInternal { error_message, error_code, is_eligible, + unified_code, + unified_message, } => Self { connector_payout_id, status: Some(status), error_message, error_code, is_eligible, + unified_code, + unified_message, ..Default::default() }, PayoutAttemptUpdate::BusinessUpdate { @@ -197,6 +212,8 @@ impl PayoutAttemptUpdate { address_id, customer_id, merchant_connector_id, + unified_code, + unified_message, } = self.into(); PayoutAttempt { payout_token: payout_token.or(source.payout_token), @@ -213,6 +230,8 @@ impl PayoutAttemptUpdate { address_id: address_id.or(source.address_id), customer_id: customer_id.or(source.customer_id), merchant_connector_id: merchant_connector_id.or(source.merchant_connector_id), + unified_code: unified_code.or(source.unified_code), + unified_message: unified_message.or(source.unified_message), ..source } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 239a2d5f2b9..9ff7c958df1 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1054,6 +1054,10 @@ diesel::table! { #[max_length = 32] merchant_connector_id -> Nullable, routing_info -> Nullable, + #[max_length = 255] + unified_code -> Nullable, + #[max_length = 1024] + unified_message -> Nullable, } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index ae3c3882393..ca62d2c477e 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -1010,6 +1010,10 @@ diesel::table! { #[max_length = 32] merchant_connector_id -> Nullable, routing_info -> Nullable, + #[max_length = 255] + unified_code -> Nullable, + #[max_length = 1024] + unified_message -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs index f747eb96c4a..9c516142844 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs @@ -1,6 +1,9 @@ use api_models::enums::PayoutConnectors; use common_enums as storage_enums; -use common_utils::id_type; +use common_utils::{ + id_type, + types::{UnifiedCode, UnifiedMessage}, +}; use serde::{Deserialize, Serialize}; use storage_enums::MerchantStorageScheme; use time::PrimitiveDateTime; @@ -78,6 +81,8 @@ pub struct PayoutAttempt { pub profile_id: id_type::ProfileId, pub merchant_connector_id: Option, pub routing_info: Option, + pub unified_code: Option, + pub unified_message: Option, } #[derive(Clone, Debug, PartialEq)] @@ -101,6 +106,8 @@ pub struct PayoutAttemptNew { pub profile_id: id_type::ProfileId, pub merchant_connector_id: Option, pub routing_info: Option, + pub unified_code: Option, + pub unified_message: Option, } #[derive(Debug, Clone)] @@ -111,6 +118,9 @@ pub enum PayoutAttemptUpdate { error_message: Option, error_code: Option, is_eligible: Option, + + unified_code: Option, + unified_message: Option, }, PayoutTokenUpdate { payout_token: String, @@ -143,6 +153,8 @@ pub struct PayoutAttemptUpdateInternal { pub address_id: Option, pub customer_id: Option, pub merchant_connector_id: Option, + pub unified_code: Option, + pub unified_message: Option, } impl From for PayoutAttemptUpdateInternal { @@ -158,12 +170,16 @@ impl From for PayoutAttemptUpdateInternal { error_message, error_code, is_eligible, + unified_code, + unified_message, } => Self { connector_payout_id, status: Some(status), error_message, error_code, is_eligible, + unified_code, + unified_message, ..Default::default() }, PayoutAttemptUpdate::BusinessUpdate { diff --git a/crates/router/src/connector/adyenplatform/transformers/payouts.rs b/crates/router/src/connector/adyenplatform/transformers/payouts.rs index e0ec8f9e18b..09772fc87de 100644 --- a/crates/router/src/connector/adyenplatform/transformers/payouts.rs +++ b/crates/router/src/connector/adyenplatform/transformers/payouts.rs @@ -270,14 +270,20 @@ impl TryFrom> item: types::PayoutsResponseRouterData, ) -> Result { let response: AdyenTransferResponse = item.response; + let status = enums::PayoutStatus::from(response.status); + + let error_code = match status { + enums::PayoutStatus::Ineligible => Some(response.reason), + _ => None, + }; Ok(Self { response: Ok(types::PayoutsResponseData { - status: Some(enums::PayoutStatus::from(response.status)), + status: Some(status), connector_payout_id: Some(response.id), payout_eligible: None, should_add_next_step_to_process_tracker: false, - error_code: None, + error_code, error_message: None, }), ..item.data diff --git a/crates/router/src/core/generic_link/payout_link/status/script.js b/crates/router/src/core/generic_link/payout_link/status/script.js index 473af49b2e7..819d7e63c37 100644 --- a/crates/router/src/core/generic_link/payout_link/status/script.js +++ b/crates/router/src/core/generic_link/payout_link/status/script.js @@ -121,10 +121,10 @@ function renderStatusDetails(payoutDetails) { "{{i18n_ref_id_text}}": payoutDetails.payout_id, }; if (typeof payoutDetails.error_code === "string") { - // resourceInfo["{{i18n_error_code_text}}"] = payoutDetails.error_code; + resourceInfo["{{i18n_error_code_text}}"] = payoutDetails.error_code; } if (typeof payoutDetails.error_message === "string") { - // resourceInfo["{{i18n_error_message}}"] = payoutDetails.error_message; + resourceInfo["{{i18n_error_message}}"] = payoutDetails.error_message; } var resourceNode = document.createElement("div"); resourceNode.id = "resource-info-container"; diff --git a/crates/router/src/core/generic_link/payout_link/status/styles.css b/crates/router/src/core/generic_link/payout_link/status/styles.css index a10bfe7e3c0..11c6e68dc41 100644 --- a/crates/router/src/core/generic_link/payout_link/status/styles.css +++ b/crates/router/src/core/generic_link/payout_link/status/styles.css @@ -80,11 +80,12 @@ body { #resource-info-container { width: 100%; border-top: 1px solid rgb(231, 234, 241); - padding: 20px 10px; + padding: 20px 0; } #resource-info { display: flex; align-items: center; + margin: 0 2.5rem; } #info-key { text-align: right; diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index a153d071088..b175c8b7c03 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -17,7 +17,10 @@ use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData}; use super::errors::{RouterResponse, StorageErrorExt}; use crate::{ configs::settings::{PaymentMethodFilterKey, PaymentMethodFilters}, - core::{payments::helpers, payouts::validator}, + core::{ + payments::helpers as payment_helpers, + payouts::{helpers as payout_helpers, validator}, + }, errors, routes::{app::StorageInterface, SessionState}, services, @@ -271,6 +274,14 @@ pub async fn initiate_payout_link( // Send back status page (_, link_utils::PayoutLinkStatus::Submitted) => { + let translated_unified_message = + payout_helpers::get_translated_unified_code_and_message( + &state, + payout_attempt.unified_code.as_ref(), + payout_attempt.unified_message.as_ref(), + &locale, + ) + .await?; let js_data = payouts::PayoutLinkStatusDetails { payout_link_id: payout_link.link_id, payout_id: payout_link.primary_reference, @@ -284,8 +295,8 @@ pub async fn initiate_payout_link( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse payout status link's return URL")?, status: payout.status, - error_code: payout_attempt.error_code, - error_message: payout_attempt.error_message, + error_code: payout_attempt.unified_code, + error_message: translated_unified_message, ui_config: ui_config_data, test_mode: link_data.test_mode.unwrap_or(false), }; @@ -338,7 +349,7 @@ pub async fn filter_payout_methods( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // Filter MCAs based on profile_id and connector_type - let filtered_mcas = helpers::filter_mca_based_on_profile_and_connector_type( + let filtered_mcas = payment_helpers::filter_mca_based_on_profile_and_connector_type( all_mcas, &payout.profile_id, common_enums::ConnectorType::PayoutProcessor, diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 85d5b78521f..d0c0b2d3cde 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -16,7 +16,7 @@ use common_utils::{ ext_traits::{AsyncExt, ValueExt}, id_type::CustomerId, link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus}, - types::MinorUnit, + types::{MinorUnit, UnifiedCode, UnifiedMessage}, }; use diesel_models::{ enums as storage_enums, @@ -71,6 +71,7 @@ pub struct PayoutData { pub profile_id: common_utils::id_type::ProfileId, pub should_terminate: bool, pub payout_link: Option, + pub current_locale: String, } // ********************************************** CORE FLOWS ********************************************** @@ -358,7 +359,7 @@ pub async fn payouts_create_core( .await? }; - response_handler(&merchant_account, &payout_data).await + response_handler(&state, &merchant_account, &payout_data).await } #[instrument(skip_all)] @@ -367,6 +368,7 @@ pub async fn payouts_confirm_core( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutCreateRequest, + locale: &str, ) -> RouterResponse { let mut payout_data = make_payout_data( &state, @@ -374,6 +376,7 @@ pub async fn payouts_confirm_core( None, &key_store, &payouts::PayoutRequest::PayoutCreateRequest(req.to_owned()), + locale, ) .await?; let payout_attempt = payout_data.payout_attempt.to_owned(); @@ -429,7 +432,7 @@ pub async fn payouts_confirm_core( ) .await?; - response_handler(&merchant_account, &payout_data).await + response_handler(&state, &merchant_account, &payout_data).await } pub async fn payouts_update_core( @@ -437,6 +440,7 @@ pub async fn payouts_update_core( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutCreateRequest, + locale: &str, ) -> RouterResponse { let payout_id = req.payout_id.clone().get_required_value("payout_id")?; let mut payout_data = make_payout_data( @@ -445,6 +449,7 @@ pub async fn payouts_update_core( None, &key_store, &payouts::PayoutRequest::PayoutCreateRequest(req.to_owned()), + locale, ) .await?; @@ -509,7 +514,7 @@ pub async fn payouts_update_core( .await?; } - response_handler(&merchant_account, &payout_data).await + response_handler(&state, &merchant_account, &payout_data).await } #[instrument(skip_all)] @@ -519,6 +524,7 @@ pub async fn payouts_retrieve_core( profile_id: Option, key_store: domain::MerchantKeyStore, req: payouts::PayoutRetrieveRequest, + locale: &str, ) -> RouterResponse { let mut payout_data = make_payout_data( &state, @@ -526,6 +532,7 @@ pub async fn payouts_retrieve_core( profile_id, &key_store, &payouts::PayoutRequest::PayoutRetrieveRequest(req.to_owned()), + locale, ) .await?; let payout_attempt = payout_data.payout_attempt.to_owned(); @@ -553,7 +560,7 @@ pub async fn payouts_retrieve_core( .await?; } - response_handler(&merchant_account, &payout_data).await + response_handler(&state, &merchant_account, &payout_data).await } #[instrument(skip_all)] @@ -562,6 +569,7 @@ pub async fn payouts_cancel_core( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutActionRequest, + locale: &str, ) -> RouterResponse { let mut payout_data = make_payout_data( &state, @@ -569,6 +577,7 @@ pub async fn payouts_cancel_core( None, &key_store, &payouts::PayoutRequest::PayoutActionRequest(req.to_owned()), + locale, ) .await?; @@ -593,6 +602,8 @@ pub async fn payouts_cancel_core( error_message: Some("Cancelled by user".to_string()), error_code: None, is_eligible: None, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = state .store @@ -643,7 +654,7 @@ pub async fn payouts_cancel_core( .attach_printable("Payout cancellation failed for given Payout request")?; } - response_handler(&merchant_account, &payout_data).await + response_handler(&state, &merchant_account, &payout_data).await } #[instrument(skip_all)] @@ -652,6 +663,7 @@ pub async fn payouts_fulfill_core( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutActionRequest, + locale: &str, ) -> RouterResponse { let mut payout_data = make_payout_data( &state, @@ -659,6 +671,7 @@ pub async fn payouts_fulfill_core( None, &key_store, &payouts::PayoutRequest::PayoutActionRequest(req.to_owned()), + locale, ) .await?; @@ -735,7 +748,7 @@ pub async fn payouts_fulfill_core( })); } - response_handler(&merchant_account, &payout_data).await + response_handler(&state, &merchant_account, &payout_data).await } #[cfg(all(feature = "olap", feature = "v2", feature = "customer_v2"))] @@ -745,6 +758,7 @@ pub async fn payouts_list_core( _profile_id_list: Option>, _key_store: domain::MerchantKeyStore, _constraints: payouts::PayoutListConstraints, + _locale: &str, ) -> RouterResponse { todo!() } @@ -760,6 +774,7 @@ pub async fn payouts_list_core( profile_id_list: Option>, key_store: domain::MerchantKeyStore, constraints: payouts::PayoutListConstraints, + _locale: &str, ) -> RouterResponse { validator::validate_payout_list_request(&constraints)?; let merchant_id = merchant_account.get_id(); @@ -878,6 +893,7 @@ pub async fn payouts_filtered_list_core( profile_id_list: Option>, key_store: domain::MerchantKeyStore, filters: payouts::PayoutListFilterConstraints, + _locale: &str, ) -> RouterResponse { let limit = &filters.limit; validator::validate_payout_list_request_for_joins(*limit)?; @@ -981,6 +997,7 @@ pub async fn payouts_list_available_filters_core( merchant_account: domain::MerchantAccount, profile_id_list: Option>, time_range: api::TimeRange, + _locale: &str, ) -> RouterResponse { let db = state.store.as_ref(); let payouts = db @@ -1293,6 +1310,8 @@ pub async fn create_recipient( error_code: None, error_message: None, is_eligible: recipient_create_data.payout_eligible, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1406,6 +1425,8 @@ pub async fn check_payout_eligibility( error_code: None, error_message: None, is_eligible: payout_response_data.payout_eligible, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1437,12 +1458,34 @@ pub async fn check_payout_eligibility( } Err(err) => { let status = storage_enums::PayoutStatus::Failed; + let (error_code, error_message) = (Some(err.code), Some(err.message)); + let (unified_code, unified_message) = helpers::get_gsm_record( + state, + error_code.clone(), + error_message.clone(), + payout_data.payout_attempt.connector.clone(), + consts::PAYOUT_FLOW_STR, + ) + .await + .map_or((None, None), |gsm| (gsm.unified_code, gsm.unified_message)); let updated_payout_attempt = storage::PayoutAttemptUpdate::StatusUpdate { connector_payout_id: payout_data.payout_attempt.connector_payout_id.to_owned(), status, - error_code: Some(err.code), - error_message: Some(err.message), + error_code, + error_message, is_eligible: Some(false), + unified_code: unified_code + .map(UnifiedCode::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_code", + })?, + unified_message: unified_message + .map(UnifiedMessage::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })?, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1497,6 +1540,8 @@ pub async fn complete_create_payout( error_code: None, error_message: None, is_eligible: None, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1591,6 +1636,8 @@ pub async fn create_payout( error_code: None, error_message: None, is_eligible: payout_response_data.payout_eligible, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1622,12 +1669,34 @@ pub async fn create_payout( } Err(err) => { let status = storage_enums::PayoutStatus::Failed; + let (error_code, error_message) = (Some(err.code), Some(err.message)); + let (unified_code, unified_message) = helpers::get_gsm_record( + state, + error_code.clone(), + error_message.clone(), + payout_data.payout_attempt.connector.clone(), + consts::PAYOUT_FLOW_STR, + ) + .await + .map_or((None, None), |gsm| (gsm.unified_code, gsm.unified_message)); let updated_payout_attempt = storage::PayoutAttemptUpdate::StatusUpdate { connector_payout_id: payout_data.payout_attempt.connector_payout_id.to_owned(), status, - error_code: Some(err.code), - error_message: Some(err.message), + error_code, + error_message, is_eligible: None, + unified_code: unified_code + .map(UnifiedCode::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_code", + })?, + unified_message: unified_message + .map(UnifiedMessage::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })?, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1774,12 +1843,37 @@ pub async fn update_retrieve_payout_tracker( .unwrap_or(payout_attempt.status.to_owned()); let updated_payout_attempt = if helpers::is_payout_err_state(status) { + let (error_code, error_message) = ( + payout_response_data.error_code.clone(), + payout_response_data.error_message.clone(), + ); + let (unified_code, unified_message) = helpers::get_gsm_record( + state, + error_code.clone(), + error_message.clone(), + payout_data.payout_attempt.connector.clone(), + consts::PAYOUT_FLOW_STR, + ) + .await + .map_or((None, None), |gsm| (gsm.unified_code, gsm.unified_message)); storage::PayoutAttemptUpdate::StatusUpdate { connector_payout_id: payout_response_data.connector_payout_id.clone(), status, - error_code: payout_response_data.error_code.clone(), - error_message: payout_response_data.error_message.clone(), + error_code, + error_message, is_eligible: payout_response_data.payout_eligible, + unified_code: unified_code + .map(UnifiedCode::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_code", + })?, + unified_message: unified_message + .map(UnifiedMessage::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })?, } } else { storage::PayoutAttemptUpdate::StatusUpdate { @@ -1788,6 +1882,8 @@ pub async fn update_retrieve_payout_tracker( error_code: None, error_message: None, is_eligible: payout_response_data.payout_eligible, + unified_code: None, + unified_message: None, } }; @@ -1886,6 +1982,8 @@ pub async fn create_recipient_disburse_account( error_code: None, error_message: None, is_eligible: payout_response_data.payout_eligible, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1899,12 +1997,34 @@ pub async fn create_recipient_disburse_account( .attach_printable("Error updating payout_attempt in db")?; } Err(err) => { + let (error_code, error_message) = (Some(err.code), Some(err.message)); + let (unified_code, unified_message) = helpers::get_gsm_record( + state, + error_code.clone(), + error_message.clone(), + payout_data.payout_attempt.connector.clone(), + consts::PAYOUT_FLOW_STR, + ) + .await + .map_or((None, None), |gsm| (gsm.unified_code, gsm.unified_message)); let updated_payout_attempt = storage::PayoutAttemptUpdate::StatusUpdate { connector_payout_id: payout_data.payout_attempt.connector_payout_id.to_owned(), status: storage_enums::PayoutStatus::Failed, - error_code: Some(err.code), - error_message: Some(err.message), + error_code, + error_message, is_eligible: None, + unified_code: unified_code + .map(UnifiedCode::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_code", + })?, + unified_message: unified_message + .map(UnifiedMessage::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })?, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1964,6 +2084,8 @@ pub async fn cancel_payout( error_code: None, error_message: None, is_eligible: payout_response_data.payout_eligible, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -1988,12 +2110,34 @@ pub async fn cancel_payout( } Err(err) => { let status = storage_enums::PayoutStatus::Failed; + let (error_code, error_message) = (Some(err.code), Some(err.message)); + let (unified_code, unified_message) = helpers::get_gsm_record( + state, + error_code.clone(), + error_message.clone(), + payout_data.payout_attempt.connector.clone(), + consts::PAYOUT_FLOW_STR, + ) + .await + .map_or((None, None), |gsm| (gsm.unified_code, gsm.unified_message)); let updated_payout_attempt = storage::PayoutAttemptUpdate::StatusUpdate { connector_payout_id: payout_data.payout_attempt.connector_payout_id.to_owned(), status, - error_code: Some(err.code), - error_message: Some(err.message), + error_code, + error_message, is_eligible: None, + unified_code: unified_code + .map(UnifiedCode::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_code", + })?, + unified_message: unified_message + .map(UnifiedMessage::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })?, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -2075,6 +2219,8 @@ pub async fn fulfill_payout( error_code: None, error_message: None, is_eligible: payout_response_data.payout_eligible, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -2131,12 +2277,34 @@ pub async fn fulfill_payout( } Err(err) => { let status = storage_enums::PayoutStatus::Failed; + let (error_code, error_message) = (Some(err.code), Some(err.message)); + let (unified_code, unified_message) = helpers::get_gsm_record( + state, + error_code.clone(), + error_message.clone(), + payout_data.payout_attempt.connector.clone(), + consts::PAYOUT_FLOW_STR, + ) + .await + .map_or((None, None), |gsm| (gsm.unified_code, gsm.unified_message)); let updated_payout_attempt = storage::PayoutAttemptUpdate::StatusUpdate { connector_payout_id: payout_data.payout_attempt.connector_payout_id.to_owned(), status, - error_code: Some(err.code), - error_message: Some(err.message), + error_code, + error_message, is_eligible: None, + unified_code: unified_code + .map(UnifiedCode::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_code", + })?, + unified_message: unified_message + .map(UnifiedMessage::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })?, }; payout_data.payout_attempt = db .update_payout_attempt( @@ -2165,6 +2333,7 @@ pub async fn fulfill_payout( } pub async fn response_handler( + state: &SessionState, merchant_account: &domain::MerchantAccount, payout_data: &PayoutData, ) -> RouterResponse { @@ -2176,12 +2345,20 @@ pub async fn response_handler( let customer_id = payouts.customer_id; let billing = billing_address.as_ref().map(From::from); + let translated_unified_message = helpers::get_translated_unified_code_and_message( + state, + payout_attempt.unified_code.as_ref(), + payout_attempt.unified_message.as_ref(), + &payout_data.current_locale, + ) + .await?; + let response = api::PayoutCreateResponse { payout_id: payouts.payout_id.to_owned(), merchant_id: merchant_account.get_id().to_owned(), amount: payouts.amount, currency: payouts.destination_currency.to_owned(), - connector: payout_attempt.connector.to_owned(), + connector: payout_attempt.connector, payout_type: payouts.payout_type.to_owned(), billing, auto_fulfill: payouts.auto_fulfill, @@ -2212,6 +2389,8 @@ pub async fn response_handler( connector_transaction_id: payout_attempt.connector_payout_id, priority: payouts.priority, attempts: None, + unified_code: payout_attempt.unified_code, + unified_message: translated_unified_message, payout_link: payout_link .map(|payout_link| { url::Url::parse(payout_link.url.peek()).map(|link| PayoutLinkResponse { @@ -2389,6 +2568,8 @@ pub async fn payout_create_db_entries( last_modified_at: common_utils::date_time::now(), merchant_connector_id: None, routing_info: None, + unified_code: None, + unified_message: None, }; let payout_attempt = db .insert_payout_attempt( @@ -2418,6 +2599,7 @@ pub async fn payout_create_db_entries( should_terminate: false, profile_id: profile_id.to_owned(), payout_link, + current_locale: locale.to_string(), }) } @@ -2428,6 +2610,7 @@ pub async fn make_payout_data( _auth_profile_id: Option, _key_store: &domain::MerchantKeyStore, _req: &payouts::PayoutRequest, + locale: &str, ) -> RouterResult { todo!() } @@ -2439,6 +2622,7 @@ pub async fn make_payout_data( auth_profile_id: Option, key_store: &domain::MerchantKeyStore, req: &payouts::PayoutRequest, + locale: &str, ) -> RouterResult { let db = &*state.store; let merchant_id = merchant_account.get_id(); @@ -2594,6 +2778,7 @@ pub async fn make_payout_data( should_terminate: false, profile_id, payout_link, + current_locale: locale.to_string(), }) } diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 913f8ec7618..ab13fa4c02d 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -8,7 +8,7 @@ use common_utils::{ fp_utils, id_type, type_name, types::{ keymanager::{Identifier, KeyManagerState}, - MinorUnit, + MinorUnit, UnifiedCode, UnifiedMessage, }, }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] @@ -948,12 +948,13 @@ pub async fn get_gsm_record( error_code: Option, error_message: Option, connector_name: Option, - flow: String, + flow: &str, ) -> Option { + let connector_name = connector_name.unwrap_or_default(); let get_gsm = || async { state.store.find_gsm_rule( - connector_name.clone().unwrap_or_default(), - flow.clone(), + connector_name.clone(), + flow.to_string(), "sub_flow".to_string(), error_code.clone().unwrap_or_default(), // TODO: make changes in connector to get a mandatory code in case of success or error response error_message.clone().unwrap_or_default(), @@ -963,7 +964,7 @@ pub async fn get_gsm_record( if err.current_context().is_db_not_found() { logger::warn!( "GSM miss for connector - {}, flow - {}, error_code - {:?}, error_message - {:?}", - connector_name.unwrap_or_default(), + connector_name, flow, error_code, error_message @@ -1269,3 +1270,29 @@ pub(super) fn get_customer_details_from_request( phone_country_code: customer_phone_code, } } + +pub async fn get_translated_unified_code_and_message( + state: &SessionState, + unified_code: Option<&UnifiedCode>, + unified_message: Option<&UnifiedMessage>, + locale: &str, +) -> CustomResult, errors::ApiErrorResponse> { + Ok(unified_code + .zip(unified_message) + .async_and_then(|(code, message)| async { + payment_helpers::get_unified_translation( + state, + code.0.clone(), + message.0.clone(), + locale.to_string(), + ) + .await + .map(UnifiedMessage::try_from) + }) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "unified_message", + })? + .or_else(|| unified_message.cloned())) +} diff --git a/crates/router/src/core/payouts/retry.rs b/crates/router/src/core/payouts/retry.rs index e190e627bdf..00c25b0d67e 100644 --- a/crates/router/src/core/payouts/retry.rs +++ b/crates/router/src/core/payouts/retry.rs @@ -190,12 +190,15 @@ pub async fn get_gsm( let error_code = payout_data.payout_attempt.error_code.to_owned(); let error_message = payout_data.payout_attempt.error_message.to_owned(); let connector_name = Some(original_connector_data.connector_name.to_string()); - let flow = "payout_flow".to_string(); - Ok( - payouts::helpers::get_gsm_record(state, error_code, error_message, connector_name, flow) - .await, + Ok(payouts::helpers::get_gsm_record( + state, + error_code, + error_message, + connector_name, + common_utils::consts::PAYOUT_FLOW_STR, ) + .await) } #[instrument(skip_all)] @@ -295,6 +298,8 @@ pub async fn modify_trackers( last_modified_at: common_utils::date_time::now(), merchant_connector_id: None, routing_info: None, + unified_code: None, + unified_message: None, }; payout_data.payout_attempt = db .insert_payout_attempt( diff --git a/crates/router/src/core/payouts/transformers.rs b/crates/router/src/core/payouts/transformers.rs index ed17eb71afa..080cf775b24 100644 --- a/crates/router/src/core/payouts/transformers.rs +++ b/crates/router/src/core/payouts/transformers.rs @@ -98,10 +98,12 @@ impl created: Some(payout.created_at), connector_transaction_id: attempt.connector_transaction_id.clone(), priority: payout.priority, - attempts: Some(vec![attempt]), billing: address, client_secret: None, payout_link: None, + unified_code: attempt.unified_code.clone(), + unified_message: attempt.unified_message.clone(), + attempts: Some(vec![attempt]), email: customer .as_ref() .and_then(|customer| customer.email.clone()), diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 8aa04a64a86..19d5591d185 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -689,6 +689,8 @@ async fn payouts_incoming_webhook_flow( error_message: None, error_code: None, is_eligible: payout_attempt.is_eligible, + unified_code: None, + unified_message: None, }; let action_req = @@ -696,9 +698,15 @@ async fn payouts_incoming_webhook_flow( payout_id: payouts.payout_id.clone(), }); - let payout_data = - payouts::make_payout_data(&state, &merchant_account, None, &key_store, &action_req) - .await?; + let payout_data = payouts::make_payout_data( + &state, + &merchant_account, + None, + &key_store, + &action_req, + common_utils::consts::DEFAULT_LOCALE, + ) + .await?; let updated_payout_attempt = db .update_payout_attempt( @@ -721,7 +729,7 @@ async fn payouts_incoming_webhook_flow( // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook if let Some(outgoing_event_type) = event_type { let router_response = - payouts::response_handler(&merchant_account, &payout_data).await?; + payouts::response_handler(&state, &merchant_account, &payout_data).await?; let payout_create_response: payout_models::PayoutCreateResponse = match router_response { diff --git a/crates/router/src/routes/payouts.rs b/crates/router/src/routes/payouts.rs index 50c18d71f2a..1b614330ebd 100644 --- a/crates/router/src/routes/payouts.rs +++ b/crates/router/src/routes/payouts.rs @@ -1,5 +1,6 @@ use actix_web::{ body::{BoxBody, MessageBody}, + http::header::HeaderMap, web, HttpRequest, HttpResponse, Responder, }; use common_enums::EntityType; @@ -20,6 +21,14 @@ use crate::{ types::api::payouts as payout_types, }; +fn get_locale_from_header(headers: &HeaderMap) -> String { + get_header_value_by_key(ACCEPT_LANGUAGE.into(), headers) + .ok() + .flatten() + .map(|val| val.to_string()) + .unwrap_or(consts::DEFAULT_LOCALE.to_string()) +} + /// Payouts - Create #[instrument(skip_all, fields(flow = ?Flow::PayoutsCreate))] pub async fn payouts_create( @@ -28,11 +37,8 @@ pub async fn payouts_create( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::PayoutsCreate; - let locale = get_header_value_by_key(ACCEPT_LANGUAGE.into(), req.headers()) - .ok() - .flatten() - .map(|val| val.to_string()) - .unwrap_or(consts::DEFAULT_LOCALE.to_string()); + let locale = get_locale_from_header(req.headers()); + Box::pin(api::server_wrap( flow, state, @@ -60,6 +66,8 @@ pub async fn payouts_retrieve( merchant_id: query_params.merchant_id.to_owned(), }; let flow = Flow::PayoutsRetrieve; + let locale = get_locale_from_header(req.headers()); + Box::pin(api::server_wrap( flow, state, @@ -72,6 +80,7 @@ pub async fn payouts_retrieve( auth.profile_id, auth.key_store, req, + &locale, ) }, auth::auth_type( @@ -95,6 +104,7 @@ pub async fn payouts_update( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::PayoutsUpdate; + let locale = get_locale_from_header(req.headers()); let payout_id = path.into_inner(); let mut payout_update_payload = json_payload.into_inner(); payout_update_payload.payout_id = Some(payout_id); @@ -104,7 +114,7 @@ pub async fn payouts_update( &req, payout_update_payload, |state, auth, req, _| { - payouts_update_core(state, auth.merchant_account, auth.key_store, req) + payouts_update_core(state, auth.merchant_account, auth.key_store, req, &locale) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, @@ -130,6 +140,7 @@ pub async fn payouts_confirm( Ok(auth) => auth, Err(e) => return api::log_and_return_error_response(e), }; + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -137,7 +148,7 @@ pub async fn payouts_confirm( &req, payload, |state, auth, req, _| { - payouts_confirm_core(state, auth.merchant_account, auth.key_store, req) + payouts_confirm_core(state, auth.merchant_account, auth.key_store, req, &locale) }, &*auth_type, api_locking::LockAction::NotApplicable, @@ -156,6 +167,7 @@ pub async fn payouts_cancel( let flow = Flow::PayoutsCancel; let mut payload = json_payload.into_inner(); payload.payout_id = path.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -163,7 +175,7 @@ pub async fn payouts_cancel( &req, payload, |state, auth, req, _| { - payouts_cancel_core(state, auth.merchant_account, auth.key_store, req) + payouts_cancel_core(state, auth.merchant_account, auth.key_store, req, &locale) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, @@ -181,6 +193,7 @@ pub async fn payouts_fulfill( let flow = Flow::PayoutsFulfill; let mut payload = json_payload.into_inner(); payload.payout_id = path.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -188,7 +201,7 @@ pub async fn payouts_fulfill( &req, payload, |state, auth, req, _| { - payouts_fulfill_core(state, auth.merchant_account, auth.key_store, req) + payouts_fulfill_core(state, auth.merchant_account, auth.key_store, req, &locale) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, @@ -206,6 +219,7 @@ pub async fn payouts_list( ) -> HttpResponse { let flow = Flow::PayoutsList; let payload = json_payload.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -213,7 +227,14 @@ pub async fn payouts_list( &req, payload, |state, auth, req, _| { - payouts_list_core(state, auth.merchant_account, None, auth.key_store, req) + payouts_list_core( + state, + auth.merchant_account, + None, + auth.key_store, + req, + &locale, + ) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -238,6 +259,7 @@ pub async fn payouts_list_profile( ) -> HttpResponse { let flow = Flow::PayoutsList; let payload = json_payload.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -251,6 +273,7 @@ pub async fn payouts_list_profile( auth.profile_id.map(|profile_id| vec![profile_id]), auth.key_store, req, + &locale, ) }, auth::auth_type( @@ -276,6 +299,7 @@ pub async fn payouts_list_by_filter( ) -> HttpResponse { let flow = Flow::PayoutsList; let payload = json_payload.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -283,7 +307,14 @@ pub async fn payouts_list_by_filter( &req, payload, |state, auth, req, _| { - payouts_filtered_list_core(state, auth.merchant_account, None, auth.key_store, req) + payouts_filtered_list_core( + state, + auth.merchant_account, + None, + auth.key_store, + req, + &locale, + ) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -308,6 +339,7 @@ pub async fn payouts_list_by_filter_profile( ) -> HttpResponse { let flow = Flow::PayoutsList; let payload = json_payload.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -321,6 +353,7 @@ pub async fn payouts_list_by_filter_profile( auth.profile_id.map(|profile_id| vec![profile_id]), auth.key_store, req, + &locale, ) }, auth::auth_type( @@ -346,6 +379,7 @@ pub async fn payouts_list_available_filters_for_merchant( ) -> HttpResponse { let flow = Flow::PayoutsFilter; let payload = json_payload.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -353,7 +387,7 @@ pub async fn payouts_list_available_filters_for_merchant( &req, payload, |state, auth, req, _| { - payouts_list_available_filters_core(state, auth.merchant_account, None, req) + payouts_list_available_filters_core(state, auth.merchant_account, None, req, &locale) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -378,6 +412,7 @@ pub async fn payouts_list_available_filters_for_profile( ) -> HttpResponse { let flow = Flow::PayoutsFilter; let payload = json_payload.into_inner(); + let locale = get_locale_from_header(req.headers()); Box::pin(api::server_wrap( flow, @@ -390,6 +425,7 @@ pub async fn payouts_list_available_filters_for_profile( auth.merchant_account, auth.profile_id.map(|profile_id| vec![profile_id]), req, + &locale, ) }, auth::auth_type( diff --git a/crates/router/src/workflows/attach_payout_account_workflow.rs b/crates/router/src/workflows/attach_payout_account_workflow.rs index fa481fdb9d6..b29a15725b0 100644 --- a/crates/router/src/workflows/attach_payout_account_workflow.rs +++ b/crates/router/src/workflows/attach_payout_account_workflow.rs @@ -1,4 +1,7 @@ -use common_utils::ext_traits::{OptionExt, ValueExt}; +use common_utils::{ + consts::DEFAULT_LOCALE, + ext_traits::{OptionExt, ValueExt}, +}; use scheduler::{ consumer::{self, workflows::ProcessTrackerWorkflow}, errors, @@ -46,8 +49,15 @@ impl ProcessTrackerWorkflow for AttachPayoutAccountWorkflow { let request = api::payouts::PayoutRequest::PayoutRetrieveRequest(tracking_data); - let mut payout_data = - payouts::make_payout_data(state, &merchant_account, None, &key_store, &request).await?; + let mut payout_data = payouts::make_payout_data( + state, + &merchant_account, + None, + &key_store, + &request, + DEFAULT_LOCALE, + ) + .await?; payouts::payouts_core( state, diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index 05b277f4f85..89fd3200d7d 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -5,7 +5,10 @@ use api_models::{ webhook_events::OutgoingWebhookRequestContent, webhooks::{OutgoingWebhook, OutgoingWebhookContent}, }; -use common_utils::ext_traits::{StringExt, ValueExt}; +use common_utils::{ + consts::DEFAULT_LOCALE, + ext_traits::{StringExt, ValueExt}, +}; use diesel_models::process_tracker::business_status; use error_stack::ResultExt; use masking::PeekInterface; @@ -521,12 +524,18 @@ async fn get_outgoing_webhook_content_and_event_type( payout_models::PayoutActionRequest { payout_id }, ); - let payout_data = - payouts::make_payout_data(&state, &merchant_account, None, &key_store, &request) - .await?; + let payout_data = payouts::make_payout_data( + &state, + &merchant_account, + None, + &key_store, + &request, + DEFAULT_LOCALE, + ) + .await?; let router_response = - payouts::response_handler(&merchant_account, &payout_data).await?; + payouts::response_handler(&state, &merchant_account, &payout_data).await?; let payout_create_response: payout_models::PayoutCreateResponse = match router_response { diff --git a/crates/storage_impl/src/payouts/payout_attempt.rs b/crates/storage_impl/src/payouts/payout_attempt.rs index 697b5d91c93..e5bfe29af54 100644 --- a/crates/storage_impl/src/payouts/payout_attempt.rs +++ b/crates/storage_impl/src/payouts/payout_attempt.rs @@ -84,6 +84,8 @@ impl PayoutAttemptInterface for KVRouterStore { profile_id: new_payout_attempt.profile_id.clone(), merchant_connector_id: new_payout_attempt.merchant_connector_id.clone(), routing_info: new_payout_attempt.routing_info.clone(), + unified_code: new_payout_attempt.unified_code.clone(), + unified_message: new_payout_attempt.unified_message.clone(), }; let redis_entry = kv::TypedSql { @@ -525,6 +527,8 @@ impl DataModelExt for PayoutAttempt { profile_id: self.profile_id, merchant_connector_id: self.merchant_connector_id, routing_info: self.routing_info, + unified_code: self.unified_code, + unified_message: self.unified_message, } } @@ -549,6 +553,8 @@ impl DataModelExt for PayoutAttempt { profile_id: storage_model.profile_id, merchant_connector_id: storage_model.merchant_connector_id, routing_info: storage_model.routing_info, + unified_code: storage_model.unified_code, + unified_message: storage_model.unified_message, } } } @@ -576,6 +582,8 @@ impl DataModelExt for PayoutAttemptNew { profile_id: self.profile_id, merchant_connector_id: self.merchant_connector_id, routing_info: self.routing_info, + unified_code: self.unified_code, + unified_message: self.unified_message, } } @@ -600,6 +608,8 @@ impl DataModelExt for PayoutAttemptNew { profile_id: storage_model.profile_id, merchant_connector_id: storage_model.merchant_connector_id, routing_info: storage_model.routing_info, + unified_code: storage_model.unified_code, + unified_message: storage_model.unified_message, } } } @@ -613,12 +623,16 @@ impl DataModelExt for PayoutAttemptUpdate { error_message, error_code, is_eligible, + unified_code, + unified_message, } => DieselPayoutAttemptUpdate::StatusUpdate { connector_payout_id, status, error_message, error_code, is_eligible, + unified_code, + unified_message, }, Self::PayoutTokenUpdate { payout_token } => { DieselPayoutAttemptUpdate::PayoutTokenUpdate { payout_token } diff --git a/migrations/2024-09-03-053218_add_unified_code_message_to_payout/down.sql b/migrations/2024-09-03-053218_add_unified_code_message_to_payout/down.sql new file mode 100644 index 00000000000..b785cad78fb --- /dev/null +++ b/migrations/2024-09-03-053218_add_unified_code_message_to_payout/down.sql @@ -0,0 +1,3 @@ +ALTER TABLE payout_attempt +DROP COLUMN IF EXISTS unified_code, +DROP COLUMN IF EXISTS unified_message; \ No newline at end of file diff --git a/migrations/2024-09-03-053218_add_unified_code_message_to_payout/up.sql b/migrations/2024-09-03-053218_add_unified_code_message_to_payout/up.sql new file mode 100644 index 00000000000..b4ccfb8e525 --- /dev/null +++ b/migrations/2024-09-03-053218_add_unified_code_message_to_payout/up.sql @@ -0,0 +1,3 @@ +ALTER TABLE payout_attempt +ADD COLUMN IF NOT EXISTS unified_code VARCHAR(255) DEFAULT NULL, +ADD COLUMN IF NOT EXISTS unified_message VARCHAR(1024) DEFAULT NULL; \ No newline at end of file From 30dd7ceb5f38849faacee5409112a8857df71972 Mon Sep 17 00:00:00 2001 From: Kashif Date: Thu, 19 Sep 2024 18:55:23 +0530 Subject: [PATCH 26/48] =?UTF-8?q?refactor(recon):=20use=20AuthDataWithUser?= =?UTF-8?q?=20and=20use=20JWTAuth=20for=20token=20verif=E2=80=A6=20(#5829)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/router/src/core/recon.rs | 12 +++++------- crates/router/src/core/user.rs | 8 +++----- crates/router/src/routes/user.rs | 5 ++++- crates/router/src/services/authentication.rs | 20 ++++---------------- 4 files changed, 16 insertions(+), 29 deletions(-) diff --git a/crates/router/src/core/recon.rs b/crates/router/src/core/recon.rs index fa9944ee8ee..2c2dfc9c9f0 100644 --- a/crates/router/src/core/recon.rs +++ b/crates/router/src/core/recon.rs @@ -17,11 +17,10 @@ use crate::{ pub async fn send_recon_request( state: SessionState, - user_with_auth_data: authentication::UserFromTokenWithAuthData, + auth_data: authentication::AuthenticationDataWithUser, ) -> RouterResponse { - let user = user_with_auth_data.0; - let user_in_db = &user_with_auth_data.1.user; - let merchant_id = user.merchant_id; + let user_in_db = &auth_data.user; + let merchant_id = auth_data.merchant_account.get_id().clone(); let user_email = user_in_db.email.clone(); let email_contents = email_types::ProFeatureRequest { @@ -55,7 +54,6 @@ pub async fn send_recon_request( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to compose and send email for ProFeatureRequest [Recon]") .async_and_then(|_| async { - let auth = user_with_auth_data.1; let updated_merchant_account = storage::MerchantAccountUpdate::ReconUpdate { recon_status: enums::ReconStatus::Requested, }; @@ -65,9 +63,9 @@ pub async fn send_recon_request( let response = db .update_merchant( key_manager_state, - auth.merchant_account, + auth_data.merchant_account, updated_merchant_account, - &auth.key_store, + &auth_data.key_store, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 9d0f168827e..89f44aa1524 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1853,11 +1853,9 @@ pub async fn verify_token( state: SessionState, user: auth::UserFromToken, ) -> UserResponse { - let user_in_db = state - .global_store - .find_user_by_id(&user.user_id) + let user_in_db = user + .get_user_from_db(&state) .await - .change_context(UserErrors::InternalServerError) .attach_printable_lazy(|| { format!( "Failed to fetch the user from DB for user_id - {}", @@ -1867,7 +1865,7 @@ pub async fn verify_token( Ok(ApplicationResponse::Json(user_api::VerifyTokenResponse { merchant_id: user.merchant_id.to_owned(), - user_email: user_in_db.email, + user_email: user_in_db.0.email, })) } diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index a6c8659617f..b1fbd2bb6df 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -575,7 +575,10 @@ pub async fn verify_recon_token(state: web::Data, http_req: HttpReques &http_req, (), |state, user, _req, _| user_core::verify_token(state, user), - &auth::DashboardNoPermissionAuth, + &auth::JWTAuth { + permission: Permission::ReconAdmin, + minimum_entity_level: EntityType::Merchant, + }, api_locking::LockAction::NotApplicable, )) .await diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 5f9fc798d89..d8318ce9517 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1984,13 +1984,9 @@ where default_auth } -#[derive(Clone)] -#[cfg(feature = "recon")] -pub struct UserFromTokenWithAuthData(pub UserFromToken, pub AuthenticationDataWithUser); - #[cfg(feature = "recon")] #[async_trait] -impl AuthenticateAndFetch for JWTAuth +impl AuthenticateAndFetch for JWTAuth where A: SessionStateInfo + Sync, { @@ -1998,7 +1994,7 @@ where &self, request_headers: &HeaderMap, state: &A, - ) -> RouterResult<(UserFromTokenWithAuthData, AuthenticationType)> { + ) -> RouterResult<(AuthenticationDataWithUser, AuthenticationType)> { let payload = parse_jwt_payload::(request_headers, state).await?; if payload.check_in_blacklist(state).await? { return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); @@ -2049,17 +2045,9 @@ where let auth_type = AuthenticationType::MerchantJwt { merchant_id: auth.merchant_account.get_id().clone(), - user_id: Some(user_id.clone()), - }; - - let user = UserFromToken { - user_id, - merchant_id: payload.merchant_id.clone(), - org_id: payload.org_id, - role_id: payload.role_id, - profile_id: payload.profile_id, + user_id: Some(user_id), }; - Ok((UserFromTokenWithAuthData(user, auth), auth_type)) + Ok((auth, auth_type)) } } From c7f7d3c16abfec84b7ec36b1b5ea75823b40a983 Mon Sep 17 00:00:00 2001 From: Jeeva Ramachandran <120017870+JeevaRamu0104@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:55:37 +0530 Subject: [PATCH 27/48] chore: remove network tokenization service config in docker compose config (#5953) --- config/docker_compose.toml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 698800f082d..f0d44db4d5e 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -597,15 +597,5 @@ recon = "recon@example.com" [network_tokenization_supported_card_networks] card_networks = "Visa, AmericanExpress, Mastercard" -[network_tokenization_service] -generate_token_url= "" -fetch_token_url= "" -token_service_api_key= "" -public_key= "" -private_key= "" -key_id= "" -delete_token_url= "" -check_token_status_url= "" - [network_tokenization_supported_connectors] connector_list = "cybersource" From 6a6ce17506932e0843140ef5b02ed201d0524d5d Mon Sep 17 00:00:00 2001 From: Debarati Ghatak <88573135+cookieg13@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:55:57 +0530 Subject: [PATCH 28/48] feat(connector): [Novalnet] add Recurring payment flow for cards (#5921) --- .../src/connectors/novalnet.rs | 16 +- .../src/connectors/novalnet/transformers.rs | 280 +++++++++------ crates/hyperswitch_connectors/src/utils.rs | 319 +++++++++++++++++- crates/router/src/configs/defaults.rs | 18 - 4 files changed, 514 insertions(+), 119 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet.rs b/crates/hyperswitch_connectors/src/connectors/novalnet.rs index 025e2bed2b8..dadf066a9a5 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet.rs @@ -1,4 +1,6 @@ pub mod transformers; +use std::collections::HashSet; + use base64::Engine; use common_enums::enums; use common_utils::{ @@ -9,6 +11,7 @@ use common_utils::{ }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ + payment_method_data::PaymentMethodData, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::{ access_token_auth::AccessTokenAuth, @@ -41,7 +44,9 @@ use masking::{ExposeInterface, Mask}; use transformers as novalnet; use crate::{ - constants::headers, types::ResponseRouterData, utils, utils::PaymentsAuthorizeRequestData, + constants::headers, + types::ResponseRouterData, + utils::{self, PaymentMethodDataType, PaymentsAuthorizeRequestData}, }; #[derive(Clone)] @@ -183,6 +188,15 @@ impl ConnectorValidation for Novalnet { Err(errors::ConnectorError::MissingConnectorTransactionID.into()) } + fn validate_mandate_payment( + &self, + pm_type: Option, + pm_data: PaymentMethodData, + ) -> CustomResult<(), errors::ConnectorError> { + let mandate_supported_pmd: HashSet = + HashSet::from([PaymentMethodDataType::Card]); + utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id()) + } } impl ConnectorRedirectResponse for Novalnet { diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs index dbdbbdb6437..b400555305d 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use cards::CardNumber; use common_enums::{enums, enums as api_enums}; use common_utils::{ + consts, ext_traits::OptionExt, pii::{Email, IpAddress}, request::Method, @@ -14,7 +15,9 @@ use hyperswitch_domain_models::{ router_data::{ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::refunds::{Execute, RSync}, router_request_types::{PaymentsCancelData, PaymentsCaptureData, PaymentsSyncData, ResponseId}, - router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, + router_response_types::{ + MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData, + }, types::{ PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, @@ -28,7 +31,7 @@ use strum::Display; use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, utils::{ - BrowserInformationData, PaymentsAuthorizeRequestData, PaymentsCancelRequestData, + self, BrowserInformationData, PaymentsAuthorizeRequestData, PaymentsCancelRequestData, PaymentsCaptureRequestData, PaymentsSyncRequestData, RefundsRequestData, RouterData as OtherRouterData, }, @@ -63,7 +66,7 @@ pub struct NovalnetPaymentsRequestMerchant { pub struct NovalnetPaymentsRequestBilling { house_no: Secret, street: Secret, - city: String, + city: Secret, zip: Secret, country_code: api_enums::CountryAlpha2, } @@ -79,7 +82,7 @@ pub struct NovalnetPaymentsRequestCustomer { } #[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct NovalNetCard { +pub struct NovalnetCard { card_number: CardNumber, card_expiry_month: Secret, card_expiry_year: Secret, @@ -87,10 +90,16 @@ pub struct NovalNetCard { card_holder: Secret, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct NovalnetMandate { + token: Secret, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(untagged)] pub enum NovalNetPaymentData { - PaymentCard(NovalNetCard), + PaymentCard(NovalnetCard), + MandatePayment(NovalnetMandate), } #[derive(Default, Debug, Serialize, Clone)] @@ -103,13 +112,14 @@ pub struct NovalnetPaymentsRequestTransaction { test_mode: i8, payment_type: NovalNetPaymentTypes, amount: StringMinorUnit, - currency: String, + currency: common_enums::Currency, order_no: String, payment_data: NovalNetPaymentData, hook_url: Option, return_url: Option, error_return_url: Option, enforce_3d: Option, //NOTE: Needed for CREDITCARD, GOOGLEPAY + create_token: Option, } #[derive(Debug, Serialize, Clone)] @@ -125,78 +135,127 @@ impl TryFrom<&NovalnetRouterData<&PaymentsAuthorizeRouterData>> for NovalnetPaym fn try_from( item: &NovalnetRouterData<&PaymentsAuthorizeRouterData>, ) -> Result { - match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::Card(req_card) => { - let auth = NovalnetAuthType::try_from(&item.router_data.connector_auth_type)?; + let auth = NovalnetAuthType::try_from(&item.router_data.connector_auth_type)?; - let merchant = NovalnetPaymentsRequestMerchant { - signature: auth.product_activation_key, - tariff: auth.tariff_id, - }; + let merchant = NovalnetPaymentsRequestMerchant { + signature: auth.product_activation_key, + tariff: auth.tariff_id, + }; - let novalnet_card = NovalNetPaymentData::PaymentCard(NovalNetCard { - card_number: req_card.card_number, - card_expiry_month: req_card.card_exp_month, - card_expiry_year: req_card.card_exp_year, - card_cvc: req_card.card_cvc, - card_holder: item.router_data.get_billing_full_name()?, - }); + let enforce_3d = match item.router_data.auth_type { + enums::AuthenticationType::ThreeDs => Some(1), + enums::AuthenticationType::NoThreeDs => None, + }; + let test_mode = match item.router_data.test_mode { + Some(true) => 1, + Some(false) | None => 0, + }; - let enforce_3d = match item.router_data.auth_type { - enums::AuthenticationType::ThreeDs => Some(1), - enums::AuthenticationType::NoThreeDs => None, - }; - let test_mode = match item.router_data.test_mode { - Some(true) => 1, - Some(false) | None => 0, - }; + let billing = NovalnetPaymentsRequestBilling { + house_no: item.router_data.get_billing_line1()?, + street: item.router_data.get_billing_line2()?, + city: Secret::new(item.router_data.get_billing_city()?), + zip: item.router_data.get_billing_zip()?, + country_code: item.router_data.get_billing_country()?, + }; + + let customer_ip = item + .router_data + .request + .get_browser_info()? + .get_ip_address()?; + + let customer = NovalnetPaymentsRequestCustomer { + first_name: item.router_data.get_billing_first_name()?, + last_name: item.router_data.get_billing_last_name()?, + email: item.router_data.get_billing_email()?, + mobile: item.router_data.get_optional_billing_phone_number(), + billing, + customer_ip, + }; + + let lang = item + .router_data + .request + .get_optional_language_from_browser_info() + .unwrap_or(consts::DEFAULT_LOCALE.to_string().to_string()); + let custom = NovalnetCustom { lang }; + let hook_url = item.router_data.request.get_webhook_url()?; + + match item + .router_data + .request + .mandate_id + .clone() + .and_then(|mandate_id| mandate_id.mandate_reference_id) + { + None => match item.router_data.request.payment_method_data { + PaymentMethodData::Card(ref req_card) => { + let novalnet_card = NovalNetPaymentData::PaymentCard(NovalnetCard { + card_number: req_card.card_number.clone(), + card_expiry_month: req_card.card_exp_month.clone(), + card_expiry_year: req_card.card_exp_year.clone(), + card_cvc: req_card.card_cvc.clone(), + card_holder: item.router_data.get_billing_full_name()?, + }); + let create_token = if item.router_data.request.is_mandate_payment() { + Some(1) + } else { + None + }; + let return_url = item.router_data.request.get_return_url()?; + + let transaction = NovalnetPaymentsRequestTransaction { + test_mode, + payment_type: NovalNetPaymentTypes::CREDITCARD, + amount: item.amount.clone(), + currency: item.router_data.request.currency, + order_no: item.router_data.connector_request_reference_id.clone(), + hook_url: Some(hook_url), + return_url: Some(return_url.clone()), + error_return_url: Some(return_url.clone()), + payment_data: novalnet_card, + enforce_3d, + create_token, + }; + + Ok(Self { + merchant, + transaction, + customer, + custom, + }) + } + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("novalnet"), + ) + .into()), + }, + Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => { + let connector_mandate_id = mandate_data.connector_mandate_id.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "connector_mandate_id", + }, + )?; + + let novalnet_mandate_data = NovalNetPaymentData::MandatePayment(NovalnetMandate { + token: Secret::new(connector_mandate_id), + }); - let return_url = item.router_data.request.get_return_url().ok(); - let hook_url = item.router_data.request.get_webhook_url().ok(); let transaction = NovalnetPaymentsRequestTransaction { test_mode, payment_type: NovalNetPaymentTypes::CREDITCARD, amount: item.amount.clone(), - currency: item.router_data.request.currency.to_string(), + currency: item.router_data.request.currency, order_no: item.router_data.connector_request_reference_id.clone(), - hook_url, - return_url: return_url.clone(), - error_return_url: return_url.clone(), - payment_data: novalnet_card, + hook_url: Some(hook_url), + return_url: None, + error_return_url: None, + payment_data: novalnet_mandate_data, enforce_3d, + create_token: None, }; - let billing = NovalnetPaymentsRequestBilling { - house_no: item.router_data.get_billing_line1()?, - street: item.router_data.get_billing_line2()?, - city: item.router_data.get_billing_city()?, - zip: item.router_data.get_billing_zip()?, - country_code: item.router_data.get_billing_country()?, - }; - - let customer_ip = item - .router_data - .request - .get_browser_info()? - .get_ip_address()?; - - let customer = NovalnetPaymentsRequestCustomer { - first_name: item.router_data.get_billing_first_name()?, - last_name: item.router_data.get_billing_last_name()?, - email: item.router_data.get_billing_email()?, - mobile: item.router_data.get_billing_phone_number().ok(), - billing, - customer_ip, - }; - - let lang = item - .router_data - .request - .get_optional_language_from_browser_info() - .unwrap_or("EN".to_string()); - - let custom = NovalnetCustom { lang }; - Ok(Self { merchant, transaction, @@ -204,7 +263,10 @@ impl TryFrom<&NovalnetRouterData<&PaymentsAuthorizeRouterData>> for NovalnetPaym custom, }) } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("novalnet"), + ) + .into()), } } } @@ -275,7 +337,7 @@ impl From for common_enums::AttemptStatus { pub struct ResultData { redirect_url: Option>, status: NovalnetAPIStatus, - status_code: i32, + status_code: u16, status_text: String, additional_message: Option, } @@ -283,7 +345,7 @@ pub struct ResultData { #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct TransactionData { payment_type: Option, - status_code: i32, + status_code: u16, txn_secret: Option, tid: Option>, test_mode: Option, @@ -378,20 +440,20 @@ impl TryFrom, - pub email: Secret, - pub first_name: Secret, - pub gender: Secret, - pub last_name: Secret, - pub mobile: Secret, + pub billing: Option, + pub customer_ip: Option>, + pub email: Option, + pub first_name: Option>, + pub gender: Option>, + pub last_name: Option>, + pub mobile: Option>, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NovalnetResponseBilling { pub city: Secret, pub country_code: Secret, - pub house_no: Secret, + pub house_no: Option>, pub street: Secret, pub zip: Secret, } @@ -404,17 +466,17 @@ pub struct NovalnetResponseMerchant { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NovalnetResponseTransactionData { - pub amount: u32, - pub currency: String, + pub amount: Option, + pub currency: Option, pub date: Option, - pub order_no: String, + pub order_no: Option, pub payment_data: NovalnetResponsePaymentData, pub payment_type: String, pub status: NovalnetTransactionStatus, pub status_code: u16, pub test_mode: u8, - pub tid: Secret, - pub txn_secret: Secret, + pub tid: Option>, + pub txn_secret: Option>, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -425,14 +487,14 @@ pub enum NovalnetResponsePaymentData { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NovalnetResponseCard { - pub card_brand: Secret, + pub card_brand: Option>, pub card_expiry_month: Secret, pub card_expiry_year: Secret, pub card_holder: Secret, pub card_number: Secret, - pub cc_3d: Secret, - pub last_four: Secret, - pub token: Option>, + pub cc_3d: Option>, + pub last_four: Option>, + pub token: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -492,7 +554,7 @@ impl TryFrom<&NovalnetRouterData<&PaymentsCaptureRouterData>> for NovalnetCaptur .router_data .request .get_optional_language_from_browser_info() - .unwrap_or("EN".to_string()), + .unwrap_or(consts::DEFAULT_LOCALE.to_string()), }; Ok(Self { transaction, @@ -527,7 +589,7 @@ impl TryFrom<&NovalnetRouterData<&RefundsRouterData>> for NovalnetRefundRe .router_data .request .get_optional_language_from_browser_info() - .unwrap_or("EN".to_string()), + .unwrap_or(consts::DEFAULT_LOCALE.to_string().to_string()), }; Ok(Self { transaction, @@ -561,7 +623,7 @@ pub struct NovalnetRefundSyncResponse { pub struct NovalnetRefundsTransactionData { amount: u32, date: Option, - currency: String, + currency: common_enums::Currency, order_no: String, payment_type: String, refund: RefundData, @@ -575,7 +637,7 @@ pub struct NovalnetRefundsTransactionData { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RefundData { amount: u32, - currency: String, + currency: common_enums::Currency, payment_type: String, tid: Option>, } @@ -667,7 +729,7 @@ impl TryFrom<&PaymentsSyncRouterData> for NovalnetSyncRequest { }; let custom = NovalnetCustom { - lang: "EN".to_string(), + lang: consts::DEFAULT_LOCALE.to_string().to_string(), }; Ok(Self { transaction, @@ -676,6 +738,18 @@ impl TryFrom<&PaymentsSyncRouterData> for NovalnetSyncRequest { } } +impl NovalnetResponseTransactionData { + pub fn get_token(transaction_data: Option<&Self>) -> Option { + if let Some(data) = transaction_data { + match &data.payment_data { + NovalnetResponsePaymentData::PaymentCard(card_data) => card_data.token.clone(), + } + } else { + None + } + } +} + impl TryFrom> for RouterData @@ -690,12 +764,16 @@ impl .response .transaction .clone() - .map(|data| data.tid.expose().to_string()); + .and_then(|data| data.tid) + .map(|tid| tid.expose().to_string()); let transaction_status = item .response .transaction + .clone() .map(|transaction_data| transaction_data.status) .unwrap_or(NovalnetTransactionStatus::Pending); + let mandate_reference_id = + NovalnetResponseTransactionData::get_token(item.response.transaction.as_ref()); Ok(Self { status: common_enums::AttemptStatus::from(transaction_status), @@ -705,7 +783,12 @@ impl .map(ResponseId::ConnectorTransactionId) .unwrap_or(ResponseId::NoResponseId), redirection_data: None, - mandate_reference: None, + mandate_reference: mandate_reference_id.as_ref().map(|id| { + MandateReference { + connector_mandate_id: Some(id.clone()), + payment_method_id: None, + } + }), connector_metadata: None, network_txn_id: None, connector_response_reference_id: transaction_id.clone(), @@ -730,7 +813,7 @@ impl pub struct CaptureTransactionData { amount: Option, capture: CaptureData, - currency: Option, + currency: Option, order_no: Option, payment_type: Option, status: Option, @@ -832,7 +915,7 @@ impl TryFrom<&RefundSyncRouterData> for NovalnetSyncRequest { lang: item .request .get_optional_language_from_browser_info() - .unwrap_or("EN".to_string()), + .unwrap_or(consts::DEFAULT_LOCALE.to_string().to_string()), }; Ok(Self { transaction, @@ -854,9 +937,8 @@ impl TryFrom> .response .transaction .clone() - .map(|transaction_data: NovalnetResponseTransactionData| { - transaction_data.tid.expose().to_string() - }) + .and_then(|data| data.tid) + .map(|tid| tid.expose().to_string()) .unwrap_or("".to_string()); //NOTE: Mapping refund_id with "" incase we dont get any tid @@ -907,7 +989,7 @@ impl TryFrom<&PaymentsCancelRouterData> for NovalnetCancelRequest { lang: item .request .get_optional_language_from_browser_info() - .unwrap_or("EN".to_string()), + .unwrap_or(consts::DEFAULT_LOCALE.to_string().to_string()), }; Ok(Self { transaction, diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index bc8502cdd8b..b2979975e55 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use api_models::payments::{self, Address, AddressDetails, OrderDetailsWithAmount, PhoneDetails}; use base64::Engine; @@ -1641,3 +1641,320 @@ mod tests { assert!(qr_image_data_source_url.is_ok()); } } + +pub fn is_mandate_supported( + selected_pmd: PaymentMethodData, + payment_method_type: Option, + mandate_implemented_pmds: HashSet, + connector: &'static str, +) -> Result<(), Error> { + if mandate_implemented_pmds.contains(&PaymentMethodDataType::from(selected_pmd.clone())) { + Ok(()) + } else { + match payment_method_type { + Some(pm_type) => Err(errors::ConnectorError::NotSupported { + message: format!("{} mandate payment", pm_type), + connector, + } + .into()), + None => Err(errors::ConnectorError::NotSupported { + message: "mandate payment".to_string(), + connector, + } + .into()), + } + } +} + +#[derive(Debug, strum::Display, Eq, PartialEq, Hash)] +pub enum PaymentMethodDataType { + Card, + Knet, + Benefit, + MomoAtm, + CardRedirect, + AliPayQr, + AliPayRedirect, + AliPayHkRedirect, + MomoRedirect, + KakaoPayRedirect, + GoPayRedirect, + GcashRedirect, + ApplePay, + ApplePayRedirect, + ApplePayThirdPartySdk, + DanaRedirect, + DuitNow, + GooglePay, + GooglePayRedirect, + GooglePayThirdPartySdk, + MbWayRedirect, + MobilePayRedirect, + PaypalRedirect, + PaypalSdk, + SamsungPay, + TwintRedirect, + VippsRedirect, + TouchNGoRedirect, + WeChatPayRedirect, + WeChatPayQr, + CashappQr, + SwishQr, + KlarnaRedirect, + KlarnaSdk, + AffirmRedirect, + AfterpayClearpayRedirect, + PayBrightRedirect, + WalleyRedirect, + AlmaRedirect, + AtomeRedirect, + BancontactCard, + Bizum, + Blik, + Eps, + Giropay, + Ideal, + Interac, + LocalBankRedirect, + OnlineBankingCzechRepublic, + OnlineBankingFinland, + OnlineBankingPoland, + OnlineBankingSlovakia, + OpenBankingUk, + Przelewy24, + Sofort, + Trustly, + OnlineBankingFpx, + OnlineBankingThailand, + AchBankDebit, + SepaBankDebit, + BecsBankDebit, + BacsBankDebit, + AchBankTransfer, + SepaBankTransfer, + BacsBankTransfer, + MultibancoBankTransfer, + PermataBankTransfer, + BcaBankTransfer, + BniVaBankTransfer, + BriVaBankTransfer, + CimbVaBankTransfer, + DanamonVaBankTransfer, + MandiriVaBankTransfer, + Pix, + Pse, + Crypto, + MandatePayment, + Reward, + Upi, + Boleto, + Efecty, + PagoEfectivo, + RedCompra, + RedPagos, + Alfamart, + Indomaret, + Oxxo, + SevenEleven, + Lawson, + MiniStop, + FamilyMart, + Seicomart, + PayEasy, + Givex, + PaySafeCar, + CardToken, + LocalBankTransfer, + Mifinity, + Fps, + PromptPay, + VietQr, + OpenBanking, + NetworkToken, +} + +impl From for PaymentMethodDataType { + fn from(pm_data: PaymentMethodData) -> Self { + match pm_data { + PaymentMethodData::Card(_) => Self::Card, + PaymentMethodData::NetworkToken(_) => Self::NetworkToken, + PaymentMethodData::CardRedirect(card_redirect_data) => { + match card_redirect_data { + hyperswitch_domain_models::payment_method_data::CardRedirectData::Knet {} => Self::Knet, + hyperswitch_domain_models::payment_method_data::CardRedirectData::Benefit {} => Self::Benefit, + hyperswitch_domain_models::payment_method_data::CardRedirectData::MomoAtm {} => Self::MomoAtm, + hyperswitch_domain_models::payment_method_data::CardRedirectData::CardRedirect {} => Self::CardRedirect, + } + } + PaymentMethodData::Wallet(wallet_data) => match wallet_data { + hyperswitch_domain_models::payment_method_data::WalletData::AliPayQr(_) => Self::AliPayQr, + hyperswitch_domain_models::payment_method_data::WalletData::AliPayRedirect(_) => Self::AliPayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::AliPayHkRedirect(_) => Self::AliPayHkRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::MomoRedirect(_) => Self::MomoRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::KakaoPayRedirect(_) => Self::KakaoPayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::GoPayRedirect(_) => Self::GoPayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::GcashRedirect(_) => Self::GcashRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::ApplePay(_) => Self::ApplePay, + hyperswitch_domain_models::payment_method_data::WalletData::ApplePayRedirect(_) => Self::ApplePayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::ApplePayThirdPartySdk(_) => { + Self::ApplePayThirdPartySdk + } + hyperswitch_domain_models::payment_method_data::WalletData::DanaRedirect {} => Self::DanaRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::GooglePay(_) => Self::GooglePay, + hyperswitch_domain_models::payment_method_data::WalletData::GooglePayRedirect(_) => Self::GooglePayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::GooglePayThirdPartySdk(_) => { + Self::GooglePayThirdPartySdk + } + hyperswitch_domain_models::payment_method_data::WalletData::MbWayRedirect(_) => Self::MbWayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::MobilePayRedirect(_) => Self::MobilePayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::PaypalRedirect(_) => Self::PaypalRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::PaypalSdk(_) => Self::PaypalSdk, + hyperswitch_domain_models::payment_method_data::WalletData::SamsungPay(_) => Self::SamsungPay, + hyperswitch_domain_models::payment_method_data::WalletData::TwintRedirect {} => Self::TwintRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::VippsRedirect {} => Self::VippsRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::TouchNGoRedirect(_) => Self::TouchNGoRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::WeChatPayRedirect(_) => Self::WeChatPayRedirect, + hyperswitch_domain_models::payment_method_data::WalletData::WeChatPayQr(_) => Self::WeChatPayQr, + hyperswitch_domain_models::payment_method_data::WalletData::CashappQr(_) => Self::CashappQr, + hyperswitch_domain_models::payment_method_data::WalletData::SwishQr(_) => Self::SwishQr, + hyperswitch_domain_models::payment_method_data::WalletData::Mifinity(_) => Self::Mifinity, + }, + PaymentMethodData::PayLater(pay_later_data) => match pay_later_data { + hyperswitch_domain_models::payment_method_data::PayLaterData::KlarnaRedirect { .. } => Self::KlarnaRedirect, + hyperswitch_domain_models::payment_method_data::PayLaterData::KlarnaSdk { .. } => Self::KlarnaSdk, + hyperswitch_domain_models::payment_method_data::PayLaterData::AffirmRedirect {} => Self::AffirmRedirect, + hyperswitch_domain_models::payment_method_data::PayLaterData::AfterpayClearpayRedirect { .. } => { + Self::AfterpayClearpayRedirect + } + hyperswitch_domain_models::payment_method_data::PayLaterData::PayBrightRedirect {} => Self::PayBrightRedirect, + hyperswitch_domain_models::payment_method_data::PayLaterData::WalleyRedirect {} => Self::WalleyRedirect, + hyperswitch_domain_models::payment_method_data::PayLaterData::AlmaRedirect {} => Self::AlmaRedirect, + hyperswitch_domain_models::payment_method_data::PayLaterData::AtomeRedirect {} => Self::AtomeRedirect, + }, + PaymentMethodData::BankRedirect(bank_redirect_data) => { + match bank_redirect_data { + hyperswitch_domain_models::payment_method_data::BankRedirectData::BancontactCard { .. } => { + Self::BancontactCard + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::Bizum {} => Self::Bizum, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Blik { .. } => Self::Blik, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Eps { .. } => Self::Eps, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Giropay { .. } => Self::Giropay, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Ideal { .. } => Self::Ideal, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Interac { .. } => Self::Interac, + hyperswitch_domain_models::payment_method_data::BankRedirectData::OnlineBankingCzechRepublic { .. } => { + Self::OnlineBankingCzechRepublic + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::OnlineBankingFinland { .. } => { + Self::OnlineBankingFinland + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::OnlineBankingPoland { .. } => { + Self::OnlineBankingPoland + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::OnlineBankingSlovakia { .. } => { + Self::OnlineBankingSlovakia + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::OpenBankingUk { .. } => Self::OpenBankingUk, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Przelewy24 { .. } => Self::Przelewy24, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Sofort { .. } => Self::Sofort, + hyperswitch_domain_models::payment_method_data::BankRedirectData::Trustly { .. } => Self::Trustly, + hyperswitch_domain_models::payment_method_data::BankRedirectData::OnlineBankingFpx { .. } => { + Self::OnlineBankingFpx + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::OnlineBankingThailand { .. } => { + Self::OnlineBankingThailand + } + hyperswitch_domain_models::payment_method_data::BankRedirectData::LocalBankRedirect { } => { + Self::LocalBankRedirect + } + } + } + PaymentMethodData::BankDebit(bank_debit_data) => { + match bank_debit_data { + hyperswitch_domain_models::payment_method_data::BankDebitData::AchBankDebit { .. } => Self::AchBankDebit, + hyperswitch_domain_models::payment_method_data::BankDebitData::SepaBankDebit { .. } => Self::SepaBankDebit, + hyperswitch_domain_models::payment_method_data::BankDebitData::BecsBankDebit { .. } => Self::BecsBankDebit, + hyperswitch_domain_models::payment_method_data::BankDebitData::BacsBankDebit { .. } => Self::BacsBankDebit, + } + } + PaymentMethodData::BankTransfer(bank_transfer_data) => { + match *bank_transfer_data { + hyperswitch_domain_models::payment_method_data::BankTransferData::AchBankTransfer { .. } => { + Self::AchBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::SepaBankTransfer { .. } => { + Self::SepaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::BacsBankTransfer { .. } => { + Self::BacsBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::MultibancoBankTransfer { .. } => { + Self::MultibancoBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::PermataBankTransfer { .. } => { + Self::PermataBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::BcaBankTransfer { .. } => { + Self::BcaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::BniVaBankTransfer { .. } => { + Self::BniVaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::BriVaBankTransfer { .. } => { + Self::BriVaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::CimbVaBankTransfer { .. } => { + Self::CimbVaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::DanamonVaBankTransfer { .. } => { + Self::DanamonVaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::MandiriVaBankTransfer { .. } => { + Self::MandiriVaBankTransfer + } + hyperswitch_domain_models::payment_method_data::BankTransferData::Pix { .. } => Self::Pix, + hyperswitch_domain_models::payment_method_data::BankTransferData::Pse {} => Self::Pse, + hyperswitch_domain_models::payment_method_data::BankTransferData::LocalBankTransfer { .. } => { + Self::LocalBankTransfer + } + } + } + PaymentMethodData::Crypto(_) => Self::Crypto, + PaymentMethodData::MandatePayment => Self::MandatePayment, + PaymentMethodData::Reward => Self::Reward, + PaymentMethodData::Upi(_) => Self::Upi, + PaymentMethodData::Voucher(voucher_data) => match voucher_data { + hyperswitch_domain_models::payment_method_data::VoucherData::Boleto(_) => Self::Boleto, + hyperswitch_domain_models::payment_method_data::VoucherData::Efecty => Self::Efecty, + hyperswitch_domain_models::payment_method_data::VoucherData::PagoEfectivo => Self::PagoEfectivo, + hyperswitch_domain_models::payment_method_data::VoucherData::RedCompra => Self::RedCompra, + hyperswitch_domain_models::payment_method_data::VoucherData::RedPagos => Self::RedPagos, + hyperswitch_domain_models::payment_method_data::VoucherData::Alfamart(_) => Self::Alfamart, + hyperswitch_domain_models::payment_method_data::VoucherData::Indomaret(_) => Self::Indomaret, + hyperswitch_domain_models::payment_method_data::VoucherData::Oxxo => Self::Oxxo, + hyperswitch_domain_models::payment_method_data::VoucherData::SevenEleven(_) => Self::SevenEleven, + hyperswitch_domain_models::payment_method_data::VoucherData::Lawson(_) => Self::Lawson, + hyperswitch_domain_models::payment_method_data::VoucherData::MiniStop(_) => Self::MiniStop, + hyperswitch_domain_models::payment_method_data::VoucherData::FamilyMart(_) => Self::FamilyMart, + hyperswitch_domain_models::payment_method_data::VoucherData::Seicomart(_) => Self::Seicomart, + hyperswitch_domain_models::payment_method_data::VoucherData::PayEasy(_) => Self::PayEasy, + }, + PaymentMethodData::RealTimePayment(real_time_payment_data) => match *real_time_payment_data{ + hyperswitch_domain_models::payment_method_data::RealTimePaymentData::DuitNow { } => Self::DuitNow, + hyperswitch_domain_models::payment_method_data::RealTimePaymentData::Fps { } => Self::Fps, + hyperswitch_domain_models::payment_method_data::RealTimePaymentData::PromptPay { } => Self::PromptPay, + hyperswitch_domain_models::payment_method_data::RealTimePaymentData::VietQr { } => Self::VietQr, + }, + PaymentMethodData::GiftCard(gift_card_data) => { + match *gift_card_data { + hyperswitch_domain_models::payment_method_data::GiftCardData::Givex(_) => Self::Givex, + hyperswitch_domain_models::payment_method_data::GiftCardData::PaySafeCard {} => Self::PaySafeCar, + } + } + PaymentMethodData::CardToken(_) => Self::CardToken, + PaymentMethodData::OpenBanking(data) => match data { + hyperswitch_domain_models::payment_method_data::OpenBankingData::OpenBankingPIS { } => Self::OpenBanking + }, + } + } +} diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index a651d27adcc..4fcb0785ea1 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -2224,15 +2224,6 @@ impl Default for super::settings::RequiredFields { field_type: enums::FieldType::UserFullName, value: None, } - ), - ( - "billing.phone.country_code".to_string(), - RequiredFieldInfo { - required_field: "payment_method_data.billing.phone.country_code".to_string(), - display_name: "dialing_code".to_string(), - field_type: enums::FieldType::UserPhoneNumberCountryCode, - value: None, - } ), ( "billing.email".to_string(), @@ -5197,15 +5188,6 @@ impl Default for super::settings::RequiredFields { field_type: enums::FieldType::UserFullName, value: None, } - ), - ( - "billing.phone.country_code".to_string(), - RequiredFieldInfo { - required_field: "payment_method_data.billing.phone.country_code".to_string(), - display_name: "dialing_code".to_string(), - field_type: enums::FieldType::UserPhoneNumberCountryCode, - value: None, - } ), ( "billing.email".to_string(), From 5942e059e9efa3fa71a13cacc896509515e2f976 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Thu, 19 Sep 2024 19:12:20 +0530 Subject: [PATCH 29/48] refactor(email): Add recipient emails in email config (#5964) --- config/config.example.toml | 12 +++++++----- config/deployments/env_specific.toml | 7 +++---- config/development.toml | 7 +++---- config/docker_compose.toml | 17 ++++++++++++++--- crates/external_services/src/email.rs | 6 ++++++ .../src/configs/secrets_transformers.rs | 1 - crates/router/src/configs/settings.rs | 8 +------- crates/router/src/consts/user.rs | 1 - crates/router/src/core/recon.rs | 2 +- .../src/core/user/dashboard_metadata.rs | 2 +- crates/router/src/services/email/types.rs | 6 +++--- .../src/utils/user/dashboard_metadata.rs | 19 ++++++++++++++----- loadtest/config/development.toml | 14 ++++++++++++-- 13 files changed, 65 insertions(+), 37 deletions(-) diff --git a/config/config.example.toml b/config/config.example.toml index be0532622bb..a6af984b01f 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -375,10 +375,12 @@ wildcard_origin = false # If true, allows any origin to make req # EmailClient configuration. Only applicable when the `email` feature flag is enabled. [email] -sender_email = "example@example.com" # Sender email -aws_region = "" # AWS region used by AWS SES -allowed_unverified_days = 1 # Number of days the api calls ( with jwt token ) can be made without verifying the email -active_email_client = "SES" # The currently active email client +sender_email = "example@example.com" # Sender email +aws_region = "" # AWS region used by AWS SES +allowed_unverified_days = 1 # Number of days the api calls ( with jwt token ) can be made without verifying the email +active_email_client = "SES" # The currently active email client +recon_recipient_email = "recon@example.com" # Recipient email for recon request email +prod_intent_recipient_email = "business@example.com" # Recipient email for prod intent email # Configuration for aws ses, applicable when the active email client is SES [email.aws_ses] @@ -745,4 +747,4 @@ delete_token_url= "" # base url to delete token from token service check_token_status_url= "" # base url to check token status from token service [network_tokenization_supported_connectors] -connector_list = "cybersource" # Supported connectors for network tokenization \ No newline at end of file +connector_list = "cybersource" # Supported connectors for network tokenization diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index 9bbf137d2ce..62350376271 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -60,6 +60,8 @@ sender_email = "example@example.com" # Sender email aws_region = "" # AWS region used by AWS SES allowed_unverified_days = 1 # Number of days the api calls ( with jwt token ) can be made without verifying the email active_email_client = "SES" # The currently active email client +recon_recipient_email = "recon@example.com" # Recipient email for recon request email +prod_intent_recipient_email = "business@example.com" # Recipient email for prod intent email # Configuration for aws ses, applicable when the active email client is SES [email.aws_ses] @@ -300,9 +302,6 @@ public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "p [user_auth_methods] encryption_key = "user_auth_table_encryption_key" # Encryption key used for encrypting data in user_authentication_methods table -[recipient_emails] -recon = "recon@example.com" - [network_tokenization_service] # Network Tokenization Service Configuration generate_token_url= "" # base url to generate token fetch_token_url= "" # base url to fetch token @@ -311,4 +310,4 @@ public_key= "" # public key to encrypt data for token service private_key= "" # private key to decrypt response payload from token service key_id= "" # key id to encrypt data for token service delete_token_url= "" # base url to delete token from token service -check_token_status_url= "" # base url to check token status from token service \ No newline at end of file +check_token_status_url= "" # base url to check token status from token service diff --git a/config/development.toml b/config/development.toml index f9f1ebc1202..43130cb78f2 100644 --- a/config/development.toml +++ b/config/development.toml @@ -301,6 +301,8 @@ sender_email = "example@example.com" aws_region = "" allowed_unverified_days = 1 active_email_client = "SES" +recon_recipient_email = "recon@example.com" +prod_intent_recipient_email = "business@example.com" [email.aws_ses] email_role_arn = "" @@ -732,11 +734,8 @@ encryption_key = "A8EF32E029BC3342E54BF2E172A4D7AA43E8EF9D2C3A624A9F04E2EF79DC69 [locker_based_open_banking_connectors] connector_list = "" -[recipient_emails] -recon = "recon@example.com" - [network_tokenization_supported_card_networks] card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] -connector_list = "cybersource" \ No newline at end of file +connector_list = "cybersource" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index f0d44db4d5e..2c6b12a88a0 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -591,11 +591,22 @@ ach = { country = "US", currency = "USD" } [locker_based_open_banking_connectors] connector_list = "" -[recipient_emails] -recon = "recon@example.com" - [network_tokenization_supported_card_networks] card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" + +# EmailClient configuration. Only applicable when the `email` feature flag is enabled. +[email] +sender_email = "example@example.com" # Sender email +aws_region = "" # AWS region used by AWS SES +allowed_unverified_days = 1 # Number of days the api calls ( with jwt token ) can be made without verifying the email +active_email_client = "SES" # The currently active email client +recon_recipient_email = "recon@example.com" # Recipient email for recon request email +prod_intent_recipient_email = "business@example.com" # Recipient email for prod intent email + +# Configuration for aws ses, applicable when the active email client is SES +[email.aws_ses] +email_role_arn = "" # The amazon resource name ( arn ) of the role which has permission to send emails +sts_role_session_name = "" # An identifier for the assumed role session, used to uniquely identify a session. diff --git a/crates/external_services/src/email.rs b/crates/external_services/src/email.rs index fc577a012c3..5751de95c12 100644 --- a/crates/external_services/src/email.rs +++ b/crates/external_services/src/email.rs @@ -137,6 +137,12 @@ pub struct EmailSettings { /// The active email client to use pub active_email_client: AvailableEmailClients, + + /// Recipient email for recon emails + pub recon_recipient_email: pii::Email, + + /// Recipient email for recon emails + pub prod_intent_recipient_email: pii::Email, } /// Errors that could occur from EmailClient. diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 604dce5047c..8c893b88319 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -498,7 +498,6 @@ pub(crate) async fn fetch_raw_secrets( decision: conf.decision, locker_based_open_banking_connectors: conf.locker_based_open_banking_connectors, grpc_client: conf.grpc_client, - recipient_emails: conf.recipient_emails, network_tokenization_supported_card_networks: conf .network_tokenization_supported_card_networks, network_tokenization_service, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 7d6dfd26eae..675b8c44e2a 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -6,7 +6,7 @@ use std::{ #[cfg(feature = "olap")] use analytics::{opensearch::OpenSearchConfig, ReportConfig}; use api_models::{enums, payment_methods::RequiredFieldInfo}; -use common_utils::{ext_traits::ConfigExt, pii::Email}; +use common_utils::ext_traits::ConfigExt; use config::{Environment, File}; use error_stack::ResultExt; #[cfg(feature = "email")] @@ -122,7 +122,6 @@ pub struct Settings { pub decision: Option, pub locker_based_open_banking_connectors: LockerBasedRecipientConnectorList, pub grpc_client: GrpcClientSettings, - pub recipient_emails: RecipientMails, pub network_tokenization_supported_card_networks: NetworkTokenizationSupportedCardNetworks, pub network_tokenization_service: Option>, pub network_tokenization_supported_connectors: NetworkTokenizationSupportedConnectors, @@ -941,11 +940,6 @@ pub struct ServerTls { pub certificate: PathBuf, } -#[derive(Debug, Deserialize, Clone, Default)] -pub struct RecipientMails { - pub recon: Email, -} - fn deserialize_hashmap_inner( value: HashMap, ) -> Result>, String> diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs index 1a2b2ef60a4..6ac2b08e6bb 100644 --- a/crates/router/src/consts/user.rs +++ b/crates/router/src/consts/user.rs @@ -5,7 +5,6 @@ pub const MAX_NAME_LENGTH: usize = 70; /// The max length of company name and merchant should be same /// because we are deriving the merchant name from company name pub const MAX_COMPANY_NAME_LENGTH: usize = MAX_ALLOWED_MERCHANT_NAME_LENGTH; -pub const BUSINESS_EMAIL: &str = "neeraj.kumar@juspay.in"; pub const RECOVERY_CODES_COUNT: usize = 8; pub const RECOVERY_CODE_LENGTH: usize = 8; // This is without counting the hyphen in between diff --git a/crates/router/src/core/recon.rs b/crates/router/src/core/recon.rs index 2c2dfc9c9f0..d5f824e0cce 100644 --- a/crates/router/src/core/recon.rs +++ b/crates/router/src/core/recon.rs @@ -33,7 +33,7 @@ pub async fn send_recon_request( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to convert recipient's email to UserEmail")?, recipient_email: domain::UserEmail::from_pii_email( - state.conf.recipient_emails.recon.clone(), + state.conf.email.recon_recipient_email.clone(), ) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to convert recipient's email to UserEmail")?, diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 47639fed717..32ad135a7d6 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -484,7 +484,7 @@ async fn insert_metadata( state.conf.proxy.https_url.as_ref(), ) .await; - logger::info!(?send_email_result); + logger::info!(prod_intent_email=?send_email_result); } } diff --git a/crates/router/src/services/email/types.rs b/crates/router/src/services/email/types.rs index 35bc9f06d9a..1c4ce79ac8c 100644 --- a/crates/router/src/services/email/types.rs +++ b/crates/router/src/services/email/types.rs @@ -413,9 +413,9 @@ pub struct BizEmailProd { impl BizEmailProd { pub fn new(state: &SessionState, data: ProdIntent) -> UserResult { Ok(Self { - recipient_email: (domain::UserEmail::new( - consts::user::BUSINESS_EMAIL.to_string().into(), - ))?, + recipient_email: domain::UserEmail::from_pii_email( + state.conf.email.prod_intent_recipient_email.clone(), + )?, settings: state.conf.clone(), subject: "New Prod Intent", user_name: data.poc_name.unwrap_or_default().into(), diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 5814f64f759..3abdfdd3abe 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -1,4 +1,4 @@ -use std::{net::IpAddr, str::FromStr}; +use std::{net::IpAddr, ops::Not, str::FromStr}; use actix_web::http::header::HeaderMap; use api_models::user::dashboard_metadata::{ @@ -11,6 +11,7 @@ use diesel_models::{ }; use error_stack::{report, ResultExt}; use masking::Secret; +use router_env::logger; use crate::{ core::errors::{UserErrors, UserResult}, @@ -284,8 +285,16 @@ fn not_contains_string(value: &Option, value_to_be_checked: &str) -> boo } pub fn is_prod_email_required(data: &ProdIntent, user_email: String) -> bool { - not_contains_string(&data.poc_email, "juspay") - && not_contains_string(&data.business_website, "juspay") - && not_contains_string(&data.business_website, "hyperswitch") - && not_contains_string(&Some(user_email), "juspay") + let poc_email_check = not_contains_string(&data.poc_email, "juspay"); + let business_website_check = not_contains_string(&data.business_website, "juspay") + && not_contains_string(&data.business_website, "hyperswitch"); + let user_email_check = not_contains_string(&Some(user_email), "juspay"); + + if (poc_email_check && business_website_check && user_email_check).not() { + logger::info!(prod_intent_email = poc_email_check); + logger::info!(prod_intent_email = business_website_check); + logger::info!(prod_intent_email = user_email_check); + } + + poc_email_check && business_website_check && user_email_check } diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index ed15e8893bd..82a21f9fc9c 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -368,5 +368,15 @@ global_tenant = { schema = "public", redis_key_prefix = "" } [multitenancy.tenants] public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "public", redis_key_prefix = "", clickhouse_database = "default"} -[recipient_emails] -recon = "recon@example.com" +[email] +sender_email = "example@example.com" +aws_region = "" +allowed_unverified_days = 1 +active_email_client = "SES" +recon_recipient_email = "recon@example.com" +prod_intent_recipient_email = "business@example.com" + +[email.aws_ses] +email_role_arn = "" +sts_role_session_name = "" + From 156a161f3eb0553dcfcc1f361e42ec7713f0fbb4 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit <64925866+apoorvdixit88@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:19:47 +0530 Subject: [PATCH 30/48] fix(payments): add time range in list payment attempts query (#5959) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 1 + api-reference/openapi_spec.json | 1 + crates/analytics/src/disputes/metrics.rs | 12 ++++-------- crates/analytics/src/opensearch.rs | 6 ++++-- crates/api_models/src/analytics.rs | 3 +-- crates/api_models/src/analytics/search.rs | 4 +--- crates/api_models/src/events.rs | 2 -- crates/api_models/src/payments.rs | 16 +--------------- crates/api_models/src/payouts.rs | 4 ++-- crates/api_models/src/refunds.rs | 4 ++-- crates/common_utils/src/events.rs | 4 +++- crates/common_utils/src/types.rs | 16 ++++++++++++++++ .../diesel_models/src/query/payment_attempt.rs | 9 +++++++++ .../src/payments/payment_attempt.rs | 1 + .../src/payments/payment_intent.rs | 8 ++++---- crates/hyperswitch_domain_models/src/payouts.rs | 4 ++-- .../src/payouts/payouts.rs | 2 +- crates/hyperswitch_domain_models/src/refunds.rs | 2 +- crates/openapi/src/openapi.rs | 2 +- crates/openapi/src/openapi_v2.rs | 2 +- crates/router/src/core/disputes.rs | 2 +- crates/router/src/core/payments.rs | 5 +++-- crates/router/src/core/payouts.rs | 2 +- crates/router/src/core/refunds.rs | 4 ++-- crates/router/src/db/dispute.rs | 6 +++--- crates/router/src/db/kafka_store.rs | 14 ++++++++------ crates/router/src/db/refund.rs | 12 ++++++------ crates/router/src/routes/disputes.rs | 6 +++--- crates/router/src/routes/payments.rs | 6 +++--- crates/router/src/routes/payouts.rs | 6 ++---- crates/router/src/routes/refunds.rs | 4 ++-- crates/router/src/types/api/payments.rs | 2 +- crates/router/src/types/storage/dispute.rs | 4 ++-- crates/router/src/types/storage/refund.rs | 8 ++++---- .../storage_impl/src/mock_db/payment_attempt.rs | 1 + .../storage_impl/src/mock_db/payment_intent.rs | 4 ++-- crates/storage_impl/src/mock_db/payouts.rs | 2 +- .../storage_impl/src/payments/payment_attempt.rs | 4 ++++ .../storage_impl/src/payments/payment_intent.rs | 8 ++++---- crates/storage_impl/src/payouts/payouts.rs | 4 ++-- 40 files changed, 111 insertions(+), 96 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 5465d902388..3690bcf24db 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -18569,6 +18569,7 @@ }, "TimeRange": { "type": "object", + "description": "A type representing a range of time for filtering, including a mandatory start time and an optional end time.", "required": [ "start_time" ], diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index ff1a93454cc..2d0a7147570 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -22888,6 +22888,7 @@ }, "TimeRange": { "type": "object", + "description": "A type representing a range of time for filtering, including a mandatory start time and an optional end time.", "required": [ "start_time" ], diff --git a/crates/analytics/src/disputes/metrics.rs b/crates/analytics/src/disputes/metrics.rs index 401c606ccf9..dd1aa3c1bbd 100644 --- a/crates/analytics/src/disputes/metrics.rs +++ b/crates/analytics/src/disputes/metrics.rs @@ -4,15 +4,11 @@ mod total_dispute_lost_amount; use std::collections::HashSet; -use api_models::{ - analytics::{ - disputes::{ - DisputeDimensions, DisputeFilters, DisputeMetrics, DisputeMetricsBucketIdentifier, - }, - Granularity, - }, - payments::TimeRange, +use api_models::analytics::{ + disputes::{DisputeDimensions, DisputeFilters, DisputeMetrics, DisputeMetricsBucketIdentifier}, + Granularity, }; +use common_utils::types::TimeRange; use diesel_models::enums as storage_enums; use time::PrimitiveDateTime; diff --git a/crates/analytics/src/opensearch.rs b/crates/analytics/src/opensearch.rs index 7486ab50a51..e0235c67bed 100644 --- a/crates/analytics/src/opensearch.rs +++ b/crates/analytics/src/opensearch.rs @@ -1,10 +1,12 @@ use api_models::{ analytics::search::SearchIndex, errors::types::{ApiError, ApiErrorResponse}, - payments::TimeRange, }; use aws_config::{self, meta::region::RegionProviderChain, Region}; -use common_utils::errors::{CustomResult, ErrorSwitch}; +use common_utils::{ + errors::{CustomResult, ErrorSwitch}, + types::TimeRange, +}; use error_stack::ResultExt; use hyperswitch_domain_models::errors::{StorageError, StorageResult}; use opensearch::{ diff --git a/crates/api_models/src/analytics.rs b/crates/api_models/src/analytics.rs index 7abef472068..0379ec09547 100644 --- a/crates/api_models/src/analytics.rs +++ b/crates/api_models/src/analytics.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +pub use common_utils::types::TimeRange; use common_utils::{events::ApiEventMetric, pii::EmailStrategy, types::authentication::AuthInfo}; use masking::Secret; @@ -14,8 +15,6 @@ use self::{ refunds::{RefundDimensions, RefundMetrics}, sdk_events::{SdkEventDimensions, SdkEventMetrics}, }; -pub use crate::payments::TimeRange; - pub mod active_payments; pub mod api_event; pub mod auth_events; diff --git a/crates/api_models/src/analytics/search.rs b/crates/api_models/src/analytics/search.rs index b962f60cae0..24dd0effcbb 100644 --- a/crates/api_models/src/analytics/search.rs +++ b/crates/api_models/src/analytics/search.rs @@ -1,9 +1,7 @@ -use common_utils::hashing::HashedString; +use common_utils::{hashing::HashedString, types::TimeRange}; use masking::WithType; use serde_json::Value; -use crate::payments::TimeRange; - #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] pub struct SearchFilters { pub payment_method: Option>, diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 13966c464d5..41e958cb3f4 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -39,8 +39,6 @@ use crate::{ verifications::*, }; -impl ApiEventMetric for TimeRange {} - impl ApiEventMetric for GetPaymentIntentFiltersRequest { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Analytics) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 715bf3c3544..b0866cfb265 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -4204,7 +4204,7 @@ pub struct PaymentListFilterConstraints { pub amount_filter: Option, /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc). #[serde(flatten)] - pub time_range: Option, + pub time_range: Option, /// The list of connectors to filter payments list pub connector: Option>, /// The list of currencies to filter payments list @@ -4295,20 +4295,6 @@ pub enum SortBy { Desc, } -#[derive( - Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema, -)] -pub struct TimeRange { - /// The start time to filter payments list or to get list of filters. To get list of filters start time is needed to be passed - #[serde(with = "common_utils::custom_serde::iso8601")] - #[serde(alias = "startTime")] - pub start_time: PrimitiveDateTime, - /// The end time to filter payments list or to get list of filters. If not passed the default time is now - #[serde(default, with = "common_utils::custom_serde::iso8601::option")] - #[serde(alias = "endTime")] - pub end_time: Option, -} - #[derive(Setter, Clone, Default, Debug, PartialEq, serde::Serialize)] pub struct VerifyResponse { pub verify_id: Option, diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index 10771df4ccd..34e1bbe5449 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -704,7 +704,7 @@ pub struct PayoutListConstraints { /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc). #[serde(flatten)] #[schema(value_type = Option)] - pub time_range: Option, + pub time_range: Option, } #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] @@ -732,7 +732,7 @@ pub struct PayoutListFilterConstraints { /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc). #[serde(flatten)] #[schema(value_type = Option)] - pub time_range: Option, + pub time_range: Option, /// The list of connectors to filter payouts list #[schema(value_type = Option>, max_length = 255, example = json!(["wise", "adyen"]))] pub connector: Option>, diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index edc0e75bdf2..237a903d80c 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; -use common_utils::pii; pub use common_utils::types::{ChargeRefunds, MinorUnit}; +use common_utils::{pii, types::TimeRange}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use utoipa::ToSchema; -use super::payments::{AmountFilter, TimeRange}; +use super::payments::AmountFilter; use crate::{ admin::{self, MerchantConnectorInfo}, enums, diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index 16d0fd9b30a..2b1571617e8 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -1,7 +1,7 @@ use common_enums::{PaymentMethod, PaymentMethodType}; use serde::Serialize; -use crate::id_type; +use crate::{id_type, types::TimeRange}; pub trait ApiEventMetric { fn get_api_event_type(&self) -> Option { @@ -139,3 +139,5 @@ impl ApiEventMetric for &T { T::get_api_event_type(self) } } + +impl ApiEventMetric for TimeRange {} diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 6acf5000ced..2206744ef0b 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -28,6 +28,7 @@ use rust_decimal::{ }; use semver::Version; use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; +use time::PrimitiveDateTime; use utoipa::ToSchema; use crate::{ @@ -582,6 +583,21 @@ impl StringMajorUnit { } } +/// A type representing a range of time for filtering, including a mandatory start time and an optional end time. +#[derive( + Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema, +)] +pub struct TimeRange { + /// The start time to filter payments list or to get list of filters. To get list of filters start time is needed to be passed + #[serde(with = "crate::custom_serde::iso8601")] + #[serde(alias = "startTime")] + pub start_time: PrimitiveDateTime, + /// The end time to filter payments list or to get list of filters. If not passed the default time is now + #[serde(default, with = "crate::custom_serde::iso8601::option")] + #[serde(alias = "endTime")] + pub end_time: Option, +} + #[cfg(test)] mod amount_conversion_tests { #![allow(clippy::unwrap_used)] diff --git a/crates/diesel_models/src/query/payment_attempt.rs b/crates/diesel_models/src/query/payment_attempt.rs index fa80b0990d1..48941330ec6 100644 --- a/crates/diesel_models/src/query/payment_attempt.rs +++ b/crates/diesel_models/src/query/payment_attempt.rs @@ -351,6 +351,7 @@ impl PaymentAttempt { payment_method: Option>, payment_method_type: Option>, authentication_type: Option>, + time_range: Option, profile_id_list: Option>, merchant_connector_id: Option>, ) -> StorageResult { @@ -360,6 +361,14 @@ impl PaymentAttempt { .filter(dsl::attempt_id.eq_any(active_attempt_ids.to_owned())) .into_boxed(); + if let Some(time_range) = time_range { + filter = filter.filter(dsl::created_at.ge(time_range.start_time)); + + if let Some(end_time) = time_range.end_time { + filter = filter.filter(dsl::created_at.le(end_time)); + } + } + if let Some(connector) = connector { filter = filter.filter(dsl::connector.eq_any(connector)); } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 24a79e2905e..003509b2039 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -156,6 +156,7 @@ pub trait PaymentAttemptInterface { payment_method_type: Option>, authentication_type: Option>, merchant_connector_id: Option>, + time_range: Option, profile_id_list: Option>, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index f5b719fe370..619ebcce68a 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -94,7 +94,7 @@ pub trait PaymentIntentInterface { &self, state: &KeyManagerState, merchant_id: &id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; @@ -108,7 +108,7 @@ pub trait PaymentIntentInterface { &self, merchant_id: &id_type::MerchantId, profile_id_list: Option>, - constraints: &api_models::payments::TimeRange, + constraints: &common_utils::types::TimeRange, ) -> error_stack::Result, errors::StorageError>; #[cfg(all( @@ -1458,8 +1458,8 @@ impl From for PaymentIntentFetchCo } } -impl From for PaymentIntentFetchConstraints { - fn from(value: api_models::payments::TimeRange) -> Self { +impl From for PaymentIntentFetchConstraints { + fn from(value: common_utils::types::TimeRange) -> Self { Self::List(Box::new(PaymentIntentListParams { offset: 0, starting_at: Some(value.start_time), diff --git a/crates/hyperswitch_domain_models/src/payouts.rs b/crates/hyperswitch_domain_models/src/payouts.rs index 952028fab30..8c6d751ebec 100644 --- a/crates/hyperswitch_domain_models/src/payouts.rs +++ b/crates/hyperswitch_domain_models/src/payouts.rs @@ -52,8 +52,8 @@ impl From for PayoutFetchConstraints } } -impl From for PayoutFetchConstraints { - fn from(value: api_models::payments::TimeRange) -> Self { +impl From for PayoutFetchConstraints { + fn from(value: common_utils::types::TimeRange) -> Self { Self::List(Box::new(PayoutListParams { offset: 0, starting_at: Some(value.start_time), diff --git a/crates/hyperswitch_domain_models/src/payouts/payouts.rs b/crates/hyperswitch_domain_models/src/payouts/payouts.rs index 42037c3c2d9..eb43089e448 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payouts.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payouts.rs @@ -67,7 +67,7 @@ pub trait PayoutsInterface { async fn filter_payouts_by_time_range_constraints( &self, _merchant_id: &id_type::MerchantId, - _time_range: &api_models::payments::TimeRange, + _time_range: &common_utils::types::TimeRange, _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; diff --git a/crates/hyperswitch_domain_models/src/refunds.rs b/crates/hyperswitch_domain_models/src/refunds.rs index 3b3a79407d0..4016754510a 100644 --- a/crates/hyperswitch_domain_models/src/refunds.rs +++ b/crates/hyperswitch_domain_models/src/refunds.rs @@ -6,7 +6,7 @@ pub struct RefundListConstraints { pub profile_id: Option>, pub limit: Option, pub offset: Option, - pub time_range: Option, + pub time_range: Option, pub amount_filter: Option, pub connector: Option>, pub merchant_connector_id: Option>, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index b626b7d4418..d1746a63dde 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -194,6 +194,7 @@ Never share your secret api keys. Keep them guarded and secure. ), components(schemas( common_utils::types::MinorUnit, + common_utils::types::TimeRange, common_utils::link_utils::GenericLinkUiConfig, common_utils::link_utils::EnabledPaymentMethod, api_models::refunds::RefundRequest, @@ -466,7 +467,6 @@ Never share your secret api keys. Keep them guarded and secure. api_models::refunds::RefundListRequest, api_models::refunds::RefundListResponse, api_models::refunds::RefundAggregateResponse, - api_models::payments::TimeRange, api_models::payments::AmountFilter, api_models::mandates::MandateRevokedResponse, api_models::mandates::MandateResponse, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index f12d6a7b567..b7771dc5e0a 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -119,6 +119,7 @@ Never share your secret api keys. Keep them guarded and secure. ), components(schemas( common_utils::types::MinorUnit, + common_utils::types::TimeRange, common_utils::link_utils::GenericLinkUiConfig, common_utils::link_utils::EnabledPaymentMethod, api_models::refunds::RefundRequest, @@ -392,7 +393,6 @@ Never share your secret api keys. Keep them guarded and secure. api_models::refunds::RefundListRequest, api_models::refunds::RefundListResponse, api_models::refunds::RefundAggregateResponse, - api_models::payments::TimeRange, api_models::payments::AmountFilter, api_models::mandates::MandateRevokedResponse, api_models::mandates::MandateResponse, diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index ff5936fc296..30b73eb4c6a 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -516,7 +516,7 @@ pub async fn get_aggregates_for_disputes( state: SessionState, merchant: domain::MerchantAccount, profile_id_list: Option>, - time_range: api::TimeRange, + time_range: common_utils::types::TimeRange, ) -> RouterResponse { let db = state.store.as_ref(); let dispute_status_with_count = db diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 30d619efedc..b8255cfcfe5 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3108,6 +3108,7 @@ pub async fn apply_filters_on_payments( constraints.payment_method_type, constraints.authentication_type, constraints.merchant_connector_id, + constraints.time_range, pi_fetch_constraints.get_profile_id_list(), merchant.storage_scheme, ) @@ -3128,7 +3129,7 @@ pub async fn get_filters_for_payments( state: SessionState, merchant: domain::MerchantAccount, merchant_key_store: domain::MerchantKeyStore, - time_range: api::TimeRange, + time_range: common_utils::types::TimeRange, ) -> RouterResponse { let db = state.store.as_ref(); let pi = db @@ -3249,7 +3250,7 @@ pub async fn get_aggregates_for_payments( state: SessionState, merchant: domain::MerchantAccount, profile_id_list: Option>, - time_range: api::TimeRange, + time_range: common_utils::types::TimeRange, ) -> RouterResponse { let db = state.store.as_ref(); let intent_status_with_count = db diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index d0c0b2d3cde..c160acb50c2 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -996,7 +996,7 @@ pub async fn payouts_list_available_filters_core( state: SessionState, merchant_account: domain::MerchantAccount, profile_id_list: Option>, - time_range: api::TimeRange, + time_range: common_utils::types::TimeRange, _locale: &str, ) -> RouterResponse { let db = state.store.as_ref(); diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index 0f7abf44b14..118f4b81766 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -904,7 +904,7 @@ pub async fn refund_list( pub async fn refund_filter_list( state: SessionState, merchant_account: domain::MerchantAccount, - req: api_models::payments::TimeRange, + req: common_utils::types::TimeRange, ) -> RouterResponse { let db = state.store; let filter_list = db @@ -1098,7 +1098,7 @@ pub async fn get_filters_for_refunds( pub async fn get_aggregates_for_refunds( state: SessionState, merchant: domain::MerchantAccount, - time_range: api::TimeRange, + time_range: common_utils::types::TimeRange, ) -> RouterResponse { let db = state.store.as_ref(); let refund_status_with_count = db diff --git a/crates/router/src/db/dispute.rs b/crates/router/src/db/dispute.rs index 1d45cfd405f..a44528fde43 100644 --- a/crates/router/src/db/dispute.rs +++ b/crates/router/src/db/dispute.rs @@ -52,7 +52,7 @@ pub trait DisputeInterface { &self, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::StorageError>; } @@ -141,7 +141,7 @@ impl DisputeInterface for Store { &self, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage::Dispute::get_dispute_status_with_count( @@ -390,7 +390,7 @@ impl DisputeInterface for MockDb { &self, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::StorageError> { let locked_disputes = self.disputes.lock().await; diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 394c48c1cd8..9b51946c5c4 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -634,7 +634,7 @@ impl DisputeInterface for KafkaStore { &self, merchant_id: &id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::StorageError> { self.diesel_store .get_dispute_status_with_count(merchant_id, profile_id_list, time_range) @@ -1605,6 +1605,7 @@ impl PaymentAttemptInterface for KafkaStore { payment_method_type: Option>, authentication_type: Option>, merchant_connector_id: Option>, + time_range: Option, profile_id_list: Option>, storage_scheme: MerchantStorageScheme, ) -> CustomResult { @@ -1617,6 +1618,7 @@ impl PaymentAttemptInterface for KafkaStore { payment_method_type, authentication_type, merchant_connector_id, + time_range, profile_id_list, storage_scheme, ) @@ -1750,7 +1752,7 @@ impl PaymentIntentInterface for KafkaStore { &self, state: &KeyManagerState, merchant_id: &id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { @@ -1770,7 +1772,7 @@ impl PaymentIntentInterface for KafkaStore { &self, merchant_id: &id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> error_stack::Result, errors::DataStorageError> { self.diesel_store .get_intent_status_with_count(merchant_id, profile_id_list, time_range) @@ -2233,7 +2235,7 @@ impl PayoutsInterface for KafkaStore { async fn filter_payouts_by_time_range_constraints( &self, merchant_id: &id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { self.diesel_store @@ -2532,7 +2534,7 @@ impl RefundInterface for KafkaStore { async fn filter_refund_by_meta_constraints( &self, merchant_id: &id_type::MerchantId, - refund_details: &api_models::payments::TimeRange, + refund_details: &common_utils::types::TimeRange, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store @@ -2544,7 +2546,7 @@ impl RefundInterface for KafkaStore { async fn get_refund_status_with_count( &self, merchant_id: &id_type::MerchantId, - constraints: &api_models::payments::TimeRange, + constraints: &common_utils::types::TimeRange, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::StorageError> { self.diesel_store diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index 808f82b00a4..dbe7534ee28 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -80,7 +80,7 @@ pub trait RefundInterface { async fn filter_refund_by_meta_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - refund_details: &api_models::payments::TimeRange, + refund_details: &common_utils::types::TimeRange, storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult; @@ -88,7 +88,7 @@ pub trait RefundInterface { async fn get_refund_status_with_count( &self, merchant_id: &common_utils::id_type::MerchantId, - constraints: &api_models::payments::TimeRange, + constraints: &common_utils::types::TimeRange, storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult, errors::StorageError>; @@ -817,7 +817,7 @@ mod storage { async fn filter_refund_by_meta_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - refund_details: &api_models::payments::TimeRange, + refund_details: &common_utils::types::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -831,7 +831,7 @@ mod storage { async fn get_refund_status_with_count( &self, merchant_id: &common_utils::id_type::MerchantId, - constraints: &api_models::payments::TimeRange, + constraints: &common_utils::types::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -1135,7 +1135,7 @@ impl RefundInterface for MockDb { async fn filter_refund_by_meta_constraints( &self, _merchant_id: &common_utils::id_type::MerchantId, - refund_details: &api_models::payments::TimeRange, + refund_details: &common_utils::types::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let refunds = self.refunds.lock().await; @@ -1182,7 +1182,7 @@ impl RefundInterface for MockDb { async fn get_refund_status_with_count( &self, _merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult, errors::StorageError> { let refunds = self.refunds.lock().await; diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index 5d8f7120f40..50a26ceae89 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -11,7 +11,7 @@ use super::app::AppState; use crate::{ core::disputes, services::{api, authentication as auth}, - types::api::{disputes as dispute_types, payments::TimeRange}, + types::api::disputes as dispute_types, }; /// Disputes - Retrieve Dispute @@ -413,7 +413,7 @@ pub async fn delete_dispute_evidence( pub async fn get_disputes_aggregate( state: web::Data, req: HttpRequest, - query_param: web::Query, + query_param: web::Query, ) -> HttpResponse { let flow = Flow::DisputesAggregate; let query_param = query_param.into_inner(); @@ -443,7 +443,7 @@ pub async fn get_disputes_aggregate( pub async fn get_disputes_aggregate_profile( state: web::Data, req: HttpRequest, - query_param: web::Query, + query_param: web::Query, ) -> HttpResponse { let flow = Flow::DisputesAggregate; let query_param = query_param.into_inner(); diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 82407976c6e..c8f4f5e4dd0 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -987,7 +987,7 @@ pub async fn profile_payments_list_by_filter( pub async fn get_filters_for_payments( state: web::Data, req: actix_web::HttpRequest, - payload: web::Json, + payload: web::Json, ) -> impl Responder { let flow = Flow::PaymentsList; let payload = payload.into_inner(); @@ -1065,7 +1065,7 @@ pub async fn get_payment_filters_profile( pub async fn get_payments_aggregates( state: web::Data, req: actix_web::HttpRequest, - payload: web::Query, + payload: web::Query, ) -> impl Responder { let flow = Flow::PaymentsAggregate; let payload = payload.into_inner(); @@ -1780,7 +1780,7 @@ impl GetLockingInput for payment_types::PaymentsManualUpdateRequest { pub async fn get_payments_aggregates_profile( state: web::Data, req: actix_web::HttpRequest, - payload: web::Query, + payload: web::Query, ) -> impl Responder { let flow = Flow::PaymentsAggregate; let payload = payload.into_inner(); diff --git a/crates/router/src/routes/payouts.rs b/crates/router/src/routes/payouts.rs index 1b614330ebd..28b2afe2bee 100644 --- a/crates/router/src/routes/payouts.rs +++ b/crates/router/src/routes/payouts.rs @@ -8,8 +8,6 @@ use common_utils::consts; use router_env::{instrument, tracing, Flow}; use super::app::AppState; -#[cfg(feature = "olap")] -use crate::types::api::payments as payment_types; use crate::{ core::{api_locking, payouts::*}, headers::ACCEPT_LANGUAGE, @@ -375,7 +373,7 @@ pub async fn payouts_list_by_filter_profile( pub async fn payouts_list_available_filters_for_merchant( state: web::Data, req: HttpRequest, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::PayoutsFilter; let payload = json_payload.into_inner(); @@ -408,7 +406,7 @@ pub async fn payouts_list_available_filters_for_merchant( pub async fn payouts_list_available_filters_for_profile( state: web::Data, req: HttpRequest, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::PayoutsFilter; let payload = json_payload.into_inner(); diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index d21c351a927..40184c5fcfa 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -318,7 +318,7 @@ pub async fn refunds_list_profile( pub async fn refunds_filter_list( state: web::Data, req: HttpRequest, - payload: web::Json, + payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsList; Box::pin(api::server_wrap( @@ -426,7 +426,7 @@ pub async fn get_refunds_filters_profile( pub async fn get_refunds_aggregates( state: web::Data, req: HttpRequest, - query_params: web::Query, + query_params: web::Query, ) -> HttpResponse { let flow = Flow::RefundsAggregate; let query_params = query_params.into_inner(); diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index 449d1c725c5..575725d4874 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -13,7 +13,7 @@ pub use api_models::payments::{ PaymentsRedirectionResponse, PaymentsRejectRequest, PaymentsRequest, PaymentsResponse, PaymentsResponseForm, PaymentsRetrieveRequest, PaymentsSessionRequest, PaymentsSessionResponse, PaymentsStartRequest, PgRedirectResponse, PhoneDetails, RedirectionResponse, SessionToken, - TimeRange, UrlDetails, VerifyRequest, VerifyResponse, WalletData, + UrlDetails, VerifyRequest, VerifyResponse, WalletData, }; use error_stack::ResultExt; pub use hyperswitch_domain_models::router_flow_types::payments::{ diff --git a/crates/router/src/types/storage/dispute.rs b/crates/router/src/types/storage/dispute.rs index 79b9a90343e..45f2073708b 100644 --- a/crates/router/src/types/storage/dispute.rs +++ b/crates/router/src/types/storage/dispute.rs @@ -19,7 +19,7 @@ pub trait DisputeDbExt: Sized { conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::DatabaseError>; } @@ -84,7 +84,7 @@ impl DisputeDbExt for Dispute { conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::DatabaseError> { let mut query = ::table() .group_by(dsl::dispute_status) diff --git a/crates/router/src/types/storage/refund.rs b/crates/router/src/types/storage/refund.rs index 8e0040abfae..350c1032261 100644 --- a/crates/router/src/types/storage/refund.rs +++ b/crates/router/src/types/storage/refund.rs @@ -29,7 +29,7 @@ pub trait RefundDbExt: Sized { async fn filter_by_meta_constraints( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, - refund_list_details: &api_models::payments::TimeRange, + refund_list_details: &common_utils::types::TimeRange, ) -> CustomResult; async fn get_refunds_count( @@ -41,7 +41,7 @@ pub trait RefundDbExt: Sized { async fn get_refund_status_with_count( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::DatabaseError>; } @@ -158,7 +158,7 @@ impl RefundDbExt for Refund { async fn filter_by_meta_constraints( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, - refund_list_details: &api_models::payments::TimeRange, + refund_list_details: &common_utils::types::TimeRange, ) -> CustomResult { let start_time = refund_list_details.start_time; @@ -299,7 +299,7 @@ impl RefundDbExt for Refund { async fn get_refund_status_with_count( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> CustomResult, errors::DatabaseError> { let mut query = ::table() .group_by(dsl::refund_status) diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index 1a4595bdbbe..7abde140991 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -52,6 +52,7 @@ impl PaymentAttemptInterface for MockDb { _payment_method_type: Option>, _authentication_type: Option>, _merchanat_connector_id: Option>, + _time_range: Option, _profile_id_list: Option>, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult { diff --git a/crates/storage_impl/src/mock_db/payment_intent.rs b/crates/storage_impl/src/mock_db/payment_intent.rs index 4cf8272650a..e0328871e33 100644 --- a/crates/storage_impl/src/mock_db/payment_intent.rs +++ b/crates/storage_impl/src/mock_db/payment_intent.rs @@ -41,7 +41,7 @@ impl PaymentIntentInterface for MockDb { &self, _state: &KeyManagerState, _merchant_id: &common_utils::id_type::MerchantId, - _time_range: &api_models::payments::TimeRange, + _time_range: &common_utils::types::TimeRange, _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, StorageError> { @@ -57,7 +57,7 @@ impl PaymentIntentInterface for MockDb { &self, _merchant_id: &common_utils::id_type::MerchantId, _profile_id_list: Option>, - _time_range: &api_models::payments::TimeRange, + _time_range: &common_utils::types::TimeRange, ) -> CustomResult, StorageError> { // [#172]: Implement function for `MockDb` Err(StorageError::MockDbError)? diff --git a/crates/storage_impl/src/mock_db/payouts.rs b/crates/storage_impl/src/mock_db/payouts.rs index 5f2cc8824e5..c72a08a1823 100644 --- a/crates/storage_impl/src/mock_db/payouts.rs +++ b/crates/storage_impl/src/mock_db/payouts.rs @@ -86,7 +86,7 @@ impl PayoutsInterface for MockDb { async fn filter_payouts_by_time_range_constraints( &self, _merchant_id: &common_utils::id_type::MerchantId, - _time_range: &api_models::payments::TimeRange, + _time_range: &common_utils::types::TimeRange, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, StorageError> { // TODO: Implement function for `MockDb` diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index d1df6861657..9974393e100 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -403,6 +403,7 @@ impl PaymentAttemptInterface for RouterStore { payment_method_type: Option>, authentication_type: Option>, merchant_connector_id: Option>, + time_range: Option, profile_id_list: Option>, _storage_scheme: MerchantStorageScheme, ) -> CustomResult { @@ -426,6 +427,7 @@ impl PaymentAttemptInterface for RouterStore { payment_method, payment_method_type, authentication_type, + time_range, profile_id_list, merchant_connector_id, ) @@ -1289,6 +1291,7 @@ impl PaymentAttemptInterface for KVRouterStore { payment_method_type: Option>, authentication_type: Option>, merchant_connector_id: Option>, + time_range: Option, profile_id_list: Option>, storage_scheme: MerchantStorageScheme, ) -> CustomResult { @@ -1301,6 +1304,7 @@ impl PaymentAttemptInterface for KVRouterStore { payment_method_type, authentication_type, merchant_connector_id, + time_range, profile_id_list, storage_scheme, ) diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index cb58eac4b61..97fa5873cae 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -378,7 +378,7 @@ impl PaymentIntentInterface for KVRouterStore { &self, state: &KeyManagerState, merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { @@ -402,7 +402,7 @@ impl PaymentIntentInterface for KVRouterStore { &self, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> error_stack::Result, StorageError> { self.router_store .get_intent_status_with_count(merchant_id, profile_id_list, time_range) @@ -747,7 +747,7 @@ impl PaymentIntentInterface for crate::RouterStore { &self, state: &KeyManagerState, merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { @@ -773,7 +773,7 @@ impl PaymentIntentInterface for crate::RouterStore { &self, merchant_id: &common_utils::id_type::MerchantId, profile_id_list: Option>, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, ) -> error_stack::Result, StorageError> { let conn = connection::pg_connection_read(self).await.switch()?; let conn = async_bb8_diesel::Connection::as_async_conn(&conn); diff --git a/crates/storage_impl/src/payouts/payouts.rs b/crates/storage_impl/src/payouts/payouts.rs index b6e1c1ac35f..2a49b01458f 100644 --- a/crates/storage_impl/src/payouts/payouts.rs +++ b/crates/storage_impl/src/payouts/payouts.rs @@ -368,7 +368,7 @@ impl PayoutsInterface for KVRouterStore { async fn filter_payouts_by_time_range_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { self.router_store @@ -772,7 +772,7 @@ impl PayoutsInterface for crate::RouterStore { async fn filter_payouts_by_time_range_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - time_range: &api_models::payments::TimeRange, + time_range: &common_utils::types::TimeRange, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { let payout_filters = (*time_range).into(); From f4fa4cdab478d33b6e9528c3649197bc5331cecd Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:29:33 +0530 Subject: [PATCH 31/48] refactor(dynamic_fields): populate `billing.email` with customer email if not present (#5962) --- crates/router/src/types/transformers.rs | 30 ++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index a6f6d7c4c5a..4ecda43f7e8 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1459,10 +1459,38 @@ impl .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "customer_details", })?; + + let mut billing_address = billing.map(api_types::Address::from); + + // This change is to fix a merchant integration + // If billing.email is not passed by the merchant, and if the customer email is present, then use the `customer.email` as the billing email + if let Some(billing_address) = &mut billing_address { + billing_address.email = billing_address.email.clone().or_else(|| { + customer + .and_then(|cust| { + cust.email + .as_ref() + .map(|email| pii::Email::from(email.clone())) + }) + .or(customer_details_from_pi.clone().and_then(|cd| cd.email)) + }); + } else { + billing_address = Some(payments::Address { + email: customer + .and_then(|cust| { + cust.email + .as_ref() + .map(|email| pii::Email::from(email.clone())) + }) + .or(customer_details_from_pi.clone().and_then(|cd| cd.email)), + ..Default::default() + }); + } + Ok(Self { currency: payment_attempt.map(|pa| pa.currency.unwrap_or_default()), shipping: shipping.map(api_types::Address::from), - billing: billing.map(api_types::Address::from), + billing: billing_address, amount: payment_attempt.map(|pa| api_types::Amount::from(pa.amount)), email: customer .and_then(|cust| cust.email.as_ref().map(|em| pii::Email::from(em.clone()))) From dee91b366aacd2a75ca69ae95f2eea9ef4749d20 Mon Sep 17 00:00:00 2001 From: Arun Raj M Date: Thu, 19 Sep 2024 23:30:25 +0530 Subject: [PATCH 32/48] refactor: Rename business profile to profiles in api, diesel, domain, interface and error types (#5877) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- ...ate-default-fallback-routing-algorithm.mdx | 3 - .../merchant-connector--list.mdx | 0 .../profile--activate-routing-algorithm.mdx} | 0 .../profile--create.mdx} | 2 +- ...profile--deactivate-routing-algorithm.mdx} | 0 ...le--retrieve-active-routing-algorithm.mdx} | 0 ...ve-default-fallback-routing-algorithm.mdx} | 0 .../profile--retrieve.mdx} | 2 +- ...ate-default-fallback-routing-algorithm.mdx | 3 + .../profile--update.mdx} | 2 +- api-reference-v2/mint.json | 20 +- api-reference-v2/openapi_spec.json | 1238 ++++++++-------- api-reference/openapi_spec.json | 1246 ++++++++--------- crates/api_models/src/admin.rs | 85 +- crates/api_models/src/events.rs | 6 +- crates/diesel_models/src/business_profile.rs | 78 +- .../src/query/business_profile.rs | 12 +- .../src/business_profile.rs | 94 +- .../src/errors/api_error_response.rs | 4 +- crates/openapi/src/openapi.rs | 18 +- crates/openapi/src/openapi_v2.rs | 30 +- crates/openapi/src/routes.rs | 2 +- crates/openapi/src/routes/disputes.rs | 4 +- crates/openapi/src/routes/merchant_account.rs | 30 +- .../src/routes/merchant_connector_account.rs | 4 +- crates/openapi/src/routes/payments.rs | 2 +- .../{business_profile.rs => profile.rs} | 311 ++-- crates/openapi/src/routes/webhook_events.rs | 6 +- .../router/src/compatibility/stripe/errors.rs | 2 +- crates/router/src/core/admin.rs | 528 ++++--- crates/router/src/core/authentication.rs | 4 +- .../src/core/authentication/transformers.rs | 2 +- .../router/src/core/authentication/utils.rs | 2 +- crates/router/src/core/mandate/helpers.rs | 2 +- crates/router/src/core/payment_link.rs | 4 +- crates/router/src/core/payment_methods.rs | 6 +- .../router/src/core/payment_methods/cards.rs | 12 +- crates/router/src/core/payments.rs | 36 +- crates/router/src/core/payments/flows.rs | 2 +- .../src/core/payments/flows/approve_flow.rs | 2 +- .../src/core/payments/flows/authorize_flow.rs | 2 +- .../src/core/payments/flows/cancel_flow.rs | 2 +- .../src/core/payments/flows/capture_flow.rs | 2 +- .../payments/flows/complete_authorize_flow.rs | 2 +- .../flows/incremental_authorization_flow.rs | 2 +- .../src/core/payments/flows/psync_flow.rs | 2 +- .../src/core/payments/flows/reject_flow.rs | 2 +- .../src/core/payments/flows/session_flow.rs | 12 +- .../payments/flows/session_update_flow.rs | 2 +- .../core/payments/flows/setup_mandate_flow.rs | 2 +- crates/router/src/core/payments/helpers.rs | 24 +- crates/router/src/core/payments/operations.rs | 20 +- .../payments/operations/payment_approve.rs | 2 +- .../payments/operations/payment_cancel.rs | 2 +- .../payments/operations/payment_capture.rs | 2 +- .../operations/payment_complete_authorize.rs | 4 +- .../payments/operations/payment_confirm.rs | 8 +- .../payments/operations/payment_create.rs | 12 +- .../payments/operations/payment_reject.rs | 2 +- .../payments/operations/payment_response.rs | 10 +- .../payments/operations/payment_session.rs | 4 +- .../core/payments/operations/payment_start.rs | 4 +- .../payments/operations/payment_status.rs | 4 +- .../payments/operations/payment_update.rs | 6 +- .../payments_incremental_authorization.rs | 4 +- .../payments/operations/tax_calculation.rs | 6 +- crates/router/src/core/payments/retry.rs | 4 +- crates/router/src/core/payments/routing.rs | 16 +- .../router/src/core/payments/tokenization.rs | 4 +- crates/router/src/core/payments/types.rs | 4 +- crates/router/src/core/payouts.rs | 8 +- crates/router/src/core/payouts/helpers.rs | 2 +- crates/router/src/core/routing.rs | 64 +- crates/router/src/core/routing/helpers.rs | 14 +- crates/router/src/core/user.rs | 12 +- crates/router/src/core/utils.rs | 8 +- crates/router/src/core/webhooks/incoming.rs | 21 +- crates/router/src/core/webhooks/outgoing.rs | 14 +- .../src/core/webhooks/webhook_events.rs | 20 +- crates/router/src/db.rs | 2 +- crates/router/src/db/business_profile.rs | 113 +- crates/router/src/db/kafka_store.rs | 38 +- crates/router/src/lib.rs | 4 +- crates/router/src/routes.rs | 10 +- crates/router/src/routes/admin.rs | 344 +---- crates/router/src/routes/app.rs | 42 +- crates/router/src/routes/lock_utils.rs | 14 +- crates/router/src/routes/profiles.rs | 381 +++++ crates/router/src/types.rs | 4 +- crates/router/src/types/api/admin.rs | 161 ++- crates/router/src/types/domain.rs | 2 +- crates/router/src/types/payment_methods.rs | 2 +- .../src/types/storage/business_profile.rs | 4 +- crates/router/src/utils.rs | 4 +- crates/router/src/utils/user/sample_data.rs | 2 +- crates/router/src/utils/user_role.rs | 2 +- crates/router/src/workflows/payment_sync.rs | 8 +- crates/router_env/src/logger/types.rs | 20 +- crates/storage_impl/src/mock_db.rs | 2 +- 99 files changed, 2643 insertions(+), 2655 deletions(-) delete mode 100644 api-reference-v2/api-reference/business-profile/business-profile--update-default-fallback-routing-algorithm.mdx rename api-reference-v2/api-reference/{business-profile => profile}/merchant-connector--list.mdx (100%) rename api-reference-v2/api-reference/{business-profile/business-profile--activate-routing-algorithm.mdx => profile/profile--activate-routing-algorithm.mdx} (100%) rename api-reference-v2/api-reference/{business-profile/business-profile--create.mdx => profile/profile--create.mdx} (88%) rename api-reference-v2/api-reference/{business-profile/business-profile--deactivate-routing-algorithm.mdx => profile/profile--deactivate-routing-algorithm.mdx} (100%) rename api-reference-v2/api-reference/{business-profile/business-profile--retrieve-active-routing-algorithm.mdx => profile/profile--retrieve-active-routing-algorithm.mdx} (100%) rename api-reference-v2/api-reference/{business-profile/business-profile--retrieve-default-fallback-routing-algorithm.mdx => profile/profile--retrieve-default-fallback-routing-algorithm.mdx} (100%) rename api-reference-v2/api-reference/{business-profile/business-profile--retrieve.mdx => profile/profile--retrieve.mdx} (91%) create mode 100644 api-reference-v2/api-reference/profile/profile--update-default-fallback-routing-algorithm.mdx rename api-reference-v2/api-reference/{business-profile/business-profile--update.mdx => profile/profile--update.mdx} (91%) rename crates/openapi/src/routes/{business_profile.rs => profile.rs} (65%) create mode 100644 crates/router/src/routes/profiles.rs diff --git a/api-reference-v2/api-reference/business-profile/business-profile--update-default-fallback-routing-algorithm.mdx b/api-reference-v2/api-reference/business-profile/business-profile--update-default-fallback-routing-algorithm.mdx deleted file mode 100644 index ca4cb5f8fe2..00000000000 --- a/api-reference-v2/api-reference/business-profile/business-profile--update-default-fallback-routing-algorithm.mdx +++ /dev/null @@ -1,3 +0,0 @@ ---- -openapi: post /v2/profiles/{profile_id}/fallback_routing ---- \ No newline at end of file diff --git a/api-reference-v2/api-reference/business-profile/merchant-connector--list.mdx b/api-reference-v2/api-reference/profile/merchant-connector--list.mdx similarity index 100% rename from api-reference-v2/api-reference/business-profile/merchant-connector--list.mdx rename to api-reference-v2/api-reference/profile/merchant-connector--list.mdx diff --git a/api-reference-v2/api-reference/business-profile/business-profile--activate-routing-algorithm.mdx b/api-reference-v2/api-reference/profile/profile--activate-routing-algorithm.mdx similarity index 100% rename from api-reference-v2/api-reference/business-profile/business-profile--activate-routing-algorithm.mdx rename to api-reference-v2/api-reference/profile/profile--activate-routing-algorithm.mdx diff --git a/api-reference-v2/api-reference/business-profile/business-profile--create.mdx b/api-reference-v2/api-reference/profile/profile--create.mdx similarity index 88% rename from api-reference-v2/api-reference/business-profile/business-profile--create.mdx rename to api-reference-v2/api-reference/profile/profile--create.mdx index 670ec1dc52a..091f245deca 100644 --- a/api-reference-v2/api-reference/business-profile/business-profile--create.mdx +++ b/api-reference-v2/api-reference/profile/profile--create.mdx @@ -1,3 +1,3 @@ --- openapi: post /v2/profiles ---- +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/business-profile/business-profile--deactivate-routing-algorithm.mdx b/api-reference-v2/api-reference/profile/profile--deactivate-routing-algorithm.mdx similarity index 100% rename from api-reference-v2/api-reference/business-profile/business-profile--deactivate-routing-algorithm.mdx rename to api-reference-v2/api-reference/profile/profile--deactivate-routing-algorithm.mdx diff --git a/api-reference-v2/api-reference/business-profile/business-profile--retrieve-active-routing-algorithm.mdx b/api-reference-v2/api-reference/profile/profile--retrieve-active-routing-algorithm.mdx similarity index 100% rename from api-reference-v2/api-reference/business-profile/business-profile--retrieve-active-routing-algorithm.mdx rename to api-reference-v2/api-reference/profile/profile--retrieve-active-routing-algorithm.mdx diff --git a/api-reference-v2/api-reference/business-profile/business-profile--retrieve-default-fallback-routing-algorithm.mdx b/api-reference-v2/api-reference/profile/profile--retrieve-default-fallback-routing-algorithm.mdx similarity index 100% rename from api-reference-v2/api-reference/business-profile/business-profile--retrieve-default-fallback-routing-algorithm.mdx rename to api-reference-v2/api-reference/profile/profile--retrieve-default-fallback-routing-algorithm.mdx diff --git a/api-reference-v2/api-reference/business-profile/business-profile--retrieve.mdx b/api-reference-v2/api-reference/profile/profile--retrieve.mdx similarity index 91% rename from api-reference-v2/api-reference/business-profile/business-profile--retrieve.mdx rename to api-reference-v2/api-reference/profile/profile--retrieve.mdx index 2a3ff1423b1..235bb7d7f50 100644 --- a/api-reference-v2/api-reference/business-profile/business-profile--retrieve.mdx +++ b/api-reference-v2/api-reference/profile/profile--retrieve.mdx @@ -1,3 +1,3 @@ --- openapi: get /v2/profiles/{profile_id} ---- +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/profile/profile--update-default-fallback-routing-algorithm.mdx b/api-reference-v2/api-reference/profile/profile--update-default-fallback-routing-algorithm.mdx new file mode 100644 index 00000000000..0ba69796a7e --- /dev/null +++ b/api-reference-v2/api-reference/profile/profile--update-default-fallback-routing-algorithm.mdx @@ -0,0 +1,3 @@ +--- +openapi: patch /v2/profiles/{profile_id}/fallback_routing +--- \ No newline at end of file diff --git a/api-reference-v2/api-reference/business-profile/business-profile--update.mdx b/api-reference-v2/api-reference/profile/profile--update.mdx similarity index 91% rename from api-reference-v2/api-reference/business-profile/business-profile--update.mdx rename to api-reference-v2/api-reference/profile/profile--update.mdx index 36f556b3eef..bf35d0afb64 100644 --- a/api-reference-v2/api-reference/business-profile/business-profile--update.mdx +++ b/api-reference-v2/api-reference/profile/profile--update.mdx @@ -1,3 +1,3 @@ --- openapi: put /v2/profiles/{profile_id} ---- +--- \ No newline at end of file diff --git a/api-reference-v2/mint.json b/api-reference-v2/mint.json index ab7635a7cf2..ae48e2b8e76 100644 --- a/api-reference-v2/mint.json +++ b/api-reference-v2/mint.json @@ -53,17 +53,17 @@ ] }, { - "group": "Business Profile", + "group": "Profile", "pages": [ - "api-reference/business-profile/business-profile--create", - "api-reference/business-profile/business-profile--update", - "api-reference/business-profile/business-profile--retrieve", - "api-reference/business-profile/merchant-connector--list", - "api-reference/business-profile/business-profile--activate-routing-algorithm", - "api-reference/business-profile/business-profile--retrieve-active-routing-algorithm", - "api-reference/business-profile/business-profile--deactivate-routing-algorithm", - "api-reference/business-profile/business-profile--update-default-fallback-routing-algorithm", - "api-reference/business-profile/business-profile--retrieve-default-fallback-routing-algorithm" + "api-reference/profile/profile--create", + "api-reference/profile/profile--update", + "api-reference/profile/profile--activate-routing-algorithm", + "api-reference/profile/profile--update-default-fallback-routing-algorithm", + "api-reference/profile/profile--deactivate-routing-algorithm", + "api-reference/profile/profile--retrieve", + "api-reference/profile/merchant-connector--list", + "api-reference/profile/profile--retrieve-active-routing-algorithm", + "api-reference/profile/profile--retrieve-default-fallback-routing-algorithm" ] }, { diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 3690bcf24db..6ff3e0176e9 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -235,7 +235,7 @@ "connector_type": "payment_processor" } }, - "Create a merchant connector account under a specific business profile": { + "Create a merchant connector account under a specific profile": { "value": { "connector_account_details": { "api_key": "{{adyen-api-key}}", @@ -624,9 +624,9 @@ "tags": [ "Merchant Account" ], - "summary": "Business Profile - List", - "description": "List business profiles for an Merchant", - "operationId": "List Business Profiles", + "summary": "Profile - List", + "description": "List profiles for an Merchant", + "operationId": "List Profiles", "parameters": [ { "name": "account_id", @@ -640,13 +640,13 @@ ], "responses": { "200": { - "description": "Business profile list retrieved successfully", + "description": "profile list retrieved successfully", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -666,19 +666,19 @@ "/v2/profiles": { "post": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Create", - "description": "Creates a new *business profile* for a merchant", - "operationId": "Create A Business Profile", + "summary": "Profile - Create", + "description": "Creates a new *profile* for a merchant", + "operationId": "Create A Profile", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileCreate" + "$ref": "#/components/schemas/ProfileCreate" }, "examples": { - "Create a business profile with profile name": { + "Create a profile with profile name": { "value": { "profile_name": "shoe_business" } @@ -690,11 +690,11 @@ }, "responses": { "200": { - "description": "Business Account Created", + "description": "Account Created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -713,16 +713,16 @@ "/v2/profiles/{profile_id}": { "get": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Retrieve", - "description": "Retrieve existing *business profile*", - "operationId": "Retrieve a Business Profile", + "summary": "Profile - Retrieve", + "description": "Retrieve existing *profile*", + "operationId": "Retrieve a Profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -731,11 +731,11 @@ ], "responses": { "200": { - "description": "Business Profile Updated", + "description": "Profile Updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -752,16 +752,16 @@ }, "put": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Update", - "description": "Update the *business profile*", - "operationId": "Update a Business Profile", + "summary": "Profile - Update", + "description": "Update the *profile*", + "operationId": "Update a Profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -772,10 +772,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileCreate" + "$ref": "#/components/schemas/ProfileCreate" }, "examples": { - "Update business profile with profile name fields": { + "Update profile with profile name fields": { "value": { "profile_name": "shoe_business" } @@ -787,11 +787,11 @@ }, "responses": { "200": { - "description": "Business Profile Updated", + "description": "Profile Updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -857,16 +857,16 @@ "/v2/profiles/{profile_id}/activate_routing_algorithm": { "patch": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Activate routing algorithm", - "description": "Activates a routing algorithm under a business profile", - "operationId": "Activates a routing algorithm under a business profile", + "summary": "Profile - Activate routing algorithm", + "description": "Activates a routing algorithm under a profile", + "operationId": "Activates a routing algorithm under a profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -924,16 +924,16 @@ "/v2/profiles/{profile_id}/deactivate_routing_algorithm": { "patch": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Deactivate routing algorithm", - "description": "Deactivates a routing algorithm under a business profile", - "operationId": " Deactivates a routing algorithm under a business profile", + "summary": "Profile - Deactivate routing algorithm", + "description": "Deactivates a routing algorithm under a profile", + "operationId": " Deactivates a routing algorithm under a profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -975,18 +975,18 @@ } }, "/v2/profiles/{profile_id}/fallback_routing": { - "post": { + "patch": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Update Default Fallback Routing Algorithm", - "description": "Update the default fallback routing algorithm for the business profile", - "operationId": "Update the default fallback routing algorithm for the business profile", + "summary": "Profile - Update Default Fallback Routing Algorithm", + "description": "Update the default fallback routing algorithm for the profile", + "operationId": "Update the default fallback routing algorithm for the profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -1041,16 +1041,16 @@ }, "get": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Retrieve Default Fallback Routing Algorithm", - "description": "Retrieve the default fallback routing algorithm for the business profile", - "operationId": "Retrieve the default fallback routing algorithm for the business profile", + "summary": "Profile - Retrieve Default Fallback Routing Algorithm", + "description": "Retrieve the default fallback routing algorithm for the profile", + "operationId": "Retrieve the default fallback routing algorithm for the profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -1088,16 +1088,16 @@ "/v2/profiles/{profile_id}/routing_algorithm": { "get": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Retrieve Active Routing Algorithm", - "description": "Retrieve active routing algorithm under the business profile", - "operationId": "Retrieve the active routing algorithm under the business profile", + "summary": "Profile - Retrieve Active Routing Algorithm", + "description": "Retrieve active routing algorithm under the profile", + "operationId": "Retrieve the active routing algorithm under the profile", "parameters": [ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -4365,677 +4365,324 @@ } ] }, - "BusinessProfileCreate": { + "CaptureMethod": { + "type": "string", + "description": "Default value if not passed is set to 'automatic' which results in Auth and Capture in one single API request. Pass 'manual' or 'manual_multiple' in case you want do a separate Auth and Capture by first authorizing and placing a hold on your customer's funds so that you can use the Payments/Capture endpoint later to capture the authorized amount. Pass 'manual' if you want to only capture the amount later once or 'manual_multiple' if you want to capture the funds multiple times later. Both 'manual' and 'manual_multiple' are only supported by a specific list of processors", + "enum": [ + "automatic", + "manual", + "manual_multiple", + "scheduled" + ] + }, + "CaptureResponse": { "type": "object", "required": [ - "profile_name" + "capture_id", + "status", + "amount", + "connector", + "authorized_attempt_id", + "capture_sequence" ], "properties": { - "profile_name": { - "type": "string", - "description": "The name of business profile", - "maxLength": 64 - }, - "return_url": { + "capture_id": { "type": "string", - "description": "The URL to redirect after the completion of the operation", - "example": "https://www.example.com/success", - "nullable": true, - "maxLength": 255 - }, - "enable_payment_response_hash": { - "type": "boolean", - "description": "A boolean value to indicate if payment response hash needs to be enabled", - "default": true, - "example": true, - "nullable": true + "description": "Unique identifier for the capture" }, - "payment_response_hash_key": { - "type": "string", - "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", - "nullable": true + "status": { + "$ref": "#/components/schemas/CaptureStatus" }, - "redirect_to_merchant_with_http_post": { - "type": "boolean", - "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", - "default": false, - "example": true, - "nullable": true + "amount": { + "type": "integer", + "format": "int64", + "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", + "example": 6540 }, - "webhook_details": { + "currency": { "allOf": [ { - "$ref": "#/components/schemas/WebhookDetails" + "$ref": "#/components/schemas/Currency" } ], "nullable": true }, - "metadata": { - "type": "object", - "description": "Metadata is useful for storing additional, unstructured information on an object.", - "nullable": true - }, - "order_fulfillment_time": { - "type": "integer", - "format": "int32", - "description": "Will be used to determine the time till which your payment will be active once the payment session starts", - "example": 900, - "nullable": true, - "minimum": 0 + "connector": { + "type": "string", + "description": "The connector used for the payment" }, - "order_fulfillment_time_origin": { - "allOf": [ - { - "$ref": "#/components/schemas/OrderFulfillmentTimeOrigin" - } - ], - "nullable": true + "authorized_attempt_id": { + "type": "string", + "description": "Unique identifier for the parent attempt on which this capture is made" }, - "applepay_verified_domains": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified Apple Pay domains for a particular profile", + "connector_capture_id": { + "type": "string", + "description": "A unique identifier for this capture provided by the connector", "nullable": true }, - "session_expiry": { + "capture_sequence": { "type": "integer", "format": "int32", - "description": "Client Secret Default expiry for all payments created under this business profile", - "example": 900, - "nullable": true, - "minimum": 0 + "description": "Sequence number of this capture, in the series of captures made for the parent attempt" }, - "payment_link_config": { - "allOf": [ - { - "$ref": "#/components/schemas/BusinessPaymentLinkConfig" - } - ], + "error_message": { + "type": "string", + "description": "If there was an error while calling the connector the error message is received here", "nullable": true }, - "authentication_connector_details": { - "allOf": [ - { - "$ref": "#/components/schemas/AuthenticationConnectorDetails" - } - ], + "error_code": { + "type": "string", + "description": "If there was an error while calling the connectors the code is received here", "nullable": true }, - "use_billing_as_payment_method_billing": { - "type": "boolean", - "description": "Whether to use the billing details passed when creating the intent as payment method billing", + "error_reason": { + "type": "string", + "description": "If there was an error while calling the connectors the reason is received here", "nullable": true }, - "collect_shipping_details_from_wallet_connector_if_required": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, + "reference_id": { + "type": "string", + "description": "Reference to the capture at connector side", "nullable": true + } + } + }, + "CaptureStatus": { + "type": "string", + "enum": [ + "started", + "charged", + "pending", + "failed" + ] + }, + "Card": { + "type": "object", + "required": [ + "card_number", + "card_exp_month", + "card_exp_year", + "card_holder_name", + "card_cvc" + ], + "properties": { + "card_number": { + "type": "string", + "description": "The card number", + "example": "4242424242424242" }, - "collect_billing_details_from_wallet_connector_if_required": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, - "nullable": true + "card_exp_month": { + "type": "string", + "description": "The card's expiry month", + "example": "24" }, - "always_collect_shipping_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, - "nullable": true + "card_exp_year": { + "type": "string", + "description": "The card's expiry year", + "example": "24" }, - "always_collect_billing_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, - "nullable": true + "card_holder_name": { + "type": "string", + "description": "The card holder's name", + "example": "John Test" }, - "is_connector_agnostic_mit_enabled": { - "type": "boolean", - "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "card_cvc": { + "type": "string", + "description": "The CVC number for the card", + "example": "242" + }, + "card_issuer": { + "type": "string", + "description": "The name of the issuer of card", + "example": "chase", "nullable": true }, - "payout_link_config": { + "card_network": { "allOf": [ { - "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + "$ref": "#/components/schemas/CardNetwork" } ], "nullable": true }, - "outgoing_webhook_custom_http_headers": { - "type": "object", - "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request. It is recommended not to use more than four key-value pairs.", + "card_type": { + "type": "string", + "example": "CREDIT", "nullable": true }, - "tax_connector_id": { + "card_issuing_country": { "type": "string", - "description": "Merchant Connector id to be stored for tax_calculator connector", + "example": "INDIA", "nullable": true }, - "is_tax_connector_enabled": { - "type": "boolean", - "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + "bank_code": { + "type": "string", + "example": "JP_AMEX", + "nullable": true }, - "is_network_tokenization_enabled": { - "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." + "nick_name": { + "type": "string", + "description": "The card holder's nick name", + "example": "John Test", + "nullable": true } - }, - "additionalProperties": false + } }, - "BusinessProfileResponse": { + "CardDetail": { "type": "object", "required": [ - "merchant_id", - "id", - "profile_name", - "enable_payment_response_hash", - "redirect_to_merchant_with_http_post", - "is_tax_connector_enabled", - "is_network_tokenization_enabled" - ], - "properties": { - "merchant_id": { + "card_number", + "card_exp_month", + "card_exp_year", + "card_holder_name" + ], + "properties": { + "card_number": { "type": "string", - "description": "The identifier for Merchant Account", - "example": "y3oqhf46pyzuxjbcn2giaqnb44", - "maxLength": 64 + "description": "Card Number", + "example": "4111111145551142" }, - "id": { + "card_exp_month": { "type": "string", - "description": "The identifier for business profile. This must be used for creating merchant accounts, payments and payouts", - "example": "pro_abcdefghijklmnopqrstuvwxyz", - "maxLength": 64 + "description": "Card Expiry Month", + "example": "10" }, - "profile_name": { + "card_exp_year": { "type": "string", - "description": "Name of the business profile", - "maxLength": 64 + "description": "Card Expiry Year", + "example": "25" }, - "return_url": { + "card_holder_name": { "type": "string", - "description": "The URL to redirect after the completion of the operation", - "example": "https://www.example.com/success", - "nullable": true, - "maxLength": 255 - }, - "enable_payment_response_hash": { - "type": "boolean", - "description": "A boolean value to indicate if payment response hash needs to be enabled", - "default": true, - "example": true + "description": "Card Holder Name", + "example": "John Doe" }, - "payment_response_hash_key": { + "nick_name": { "type": "string", - "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", + "description": "Card Holder's Nick Name", + "example": "John Doe", "nullable": true }, - "redirect_to_merchant_with_http_post": { - "type": "boolean", - "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", - "default": false, - "example": true + "card_issuing_country": { + "type": "string", + "description": "Card Issuing Country", + "nullable": true }, - "webhook_details": { + "card_network": { "allOf": [ { - "$ref": "#/components/schemas/WebhookDetails" + "$ref": "#/components/schemas/CardNetwork" } ], "nullable": true }, - "metadata": { - "type": "object", - "description": "Metadata is useful for storing additional, unstructured information on an object.", - "nullable": true - }, - "applepay_verified_domains": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified Apple Pay domains for a particular profile", + "card_issuer": { + "type": "string", + "description": "Issuer Bank for Card", "nullable": true }, - "session_expiry": { - "type": "integer", - "format": "int64", - "description": "Client Secret Default expiry for all payments created under this business profile", - "example": 900, + "card_type": { + "type": "string", + "description": "Card Type", "nullable": true - }, - "payment_link_config": { - "allOf": [ - { - "$ref": "#/components/schemas/BusinessPaymentLinkConfig" - } - ], + } + }, + "additionalProperties": false + }, + "CardDetailFromLocker": { + "type": "object", + "required": [ + "saved_to_locker" + ], + "properties": { + "scheme": { + "type": "string", "nullable": true }, - "authentication_connector_details": { - "allOf": [ - { - "$ref": "#/components/schemas/AuthenticationConnectorDetails" - } - ], + "issuer_country": { + "type": "string", "nullable": true }, - "use_billing_as_payment_method_billing": { - "type": "boolean", + "last4_digits": { + "type": "string", "nullable": true }, - "extended_card_info_config": { - "allOf": [ - { - "$ref": "#/components/schemas/ExtendedCardInfoConfig" - } - ], + "expiry_month": { + "type": "string", "nullable": true }, - "collect_shipping_details_from_wallet_connector_if_required": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, + "expiry_year": { + "type": "string", "nullable": true }, - "collect_billing_details_from_wallet_connector_if_required": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, + "card_token": { + "type": "string", "nullable": true }, - "always_collect_shipping_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, + "card_holder_name": { + "type": "string", "nullable": true }, - "always_collect_billing_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, + "card_fingerprint": { + "type": "string", "nullable": true }, - "is_connector_agnostic_mit_enabled": { - "type": "boolean", - "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "nick_name": { + "type": "string", "nullable": true }, - "payout_link_config": { + "card_network": { "allOf": [ { - "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + "$ref": "#/components/schemas/CardNetwork" } ], "nullable": true }, - "outgoing_webhook_custom_http_headers": { - "type": "object", - "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request.", + "card_isin": { + "type": "string", "nullable": true }, - "order_fulfillment_time": { - "type": "integer", - "format": "int32", - "description": "Will be used to determine the time till which your payment will be active once the payment session starts", - "example": 900, - "nullable": true, - "minimum": 0 - }, - "order_fulfillment_time_origin": { - "allOf": [ - { - "$ref": "#/components/schemas/OrderFulfillmentTimeOrigin" - } - ], + "card_issuer": { + "type": "string", "nullable": true }, - "tax_connector_id": { + "card_type": { "type": "string", - "description": "Merchant Connector id to be stored for tax_calculator connector", "nullable": true }, - "is_tax_connector_enabled": { - "type": "boolean", - "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." - }, - "is_network_tokenization_enabled": { - "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", - "default": false, - "example": false + "saved_to_locker": { + "type": "boolean" } } }, - "CaptureMethod": { - "type": "string", - "description": "Default value if not passed is set to 'automatic' which results in Auth and Capture in one single API request. Pass 'manual' or 'manual_multiple' in case you want do a separate Auth and Capture by first authorizing and placing a hold on your customer's funds so that you can use the Payments/Capture endpoint later to capture the authorized amount. Pass 'manual' if you want to only capture the amount later once or 'manual_multiple' if you want to capture the funds multiple times later. Both 'manual' and 'manual_multiple' are only supported by a specific list of processors", - "enum": [ - "automatic", - "manual", - "manual_multiple", - "scheduled" - ] - }, - "CaptureResponse": { + "CardDetailUpdate": { "type": "object", "required": [ - "capture_id", - "status", - "amount", - "connector", - "authorized_attempt_id", - "capture_sequence" + "card_exp_month", + "card_exp_year", + "card_holder_name" ], "properties": { - "capture_id": { - "type": "string", - "description": "Unique identifier for the capture" - }, - "status": { - "$ref": "#/components/schemas/CaptureStatus" - }, - "amount": { - "type": "integer", - "format": "int64", - "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", - "example": 6540 - }, - "currency": { - "allOf": [ - { - "$ref": "#/components/schemas/Currency" - } - ], - "nullable": true - }, - "connector": { + "card_exp_month": { "type": "string", - "description": "The connector used for the payment" + "description": "Card Expiry Month", + "example": "10" }, - "authorized_attempt_id": { + "card_exp_year": { "type": "string", - "description": "Unique identifier for the parent attempt on which this capture is made" + "description": "Card Expiry Year", + "example": "25" }, - "connector_capture_id": { + "card_holder_name": { "type": "string", - "description": "A unique identifier for this capture provided by the connector", - "nullable": true - }, - "capture_sequence": { - "type": "integer", - "format": "int32", - "description": "Sequence number of this capture, in the series of captures made for the parent attempt" + "description": "Card Holder Name", + "example": "John Doe" }, - "error_message": { + "nick_name": { "type": "string", - "description": "If there was an error while calling the connector the error message is received here", - "nullable": true - }, - "error_code": { - "type": "string", - "description": "If there was an error while calling the connectors the code is received here", - "nullable": true - }, - "error_reason": { - "type": "string", - "description": "If there was an error while calling the connectors the reason is received here", - "nullable": true - }, - "reference_id": { - "type": "string", - "description": "Reference to the capture at connector side", - "nullable": true - } - } - }, - "CaptureStatus": { - "type": "string", - "enum": [ - "started", - "charged", - "pending", - "failed" - ] - }, - "Card": { - "type": "object", - "required": [ - "card_number", - "card_exp_month", - "card_exp_year", - "card_holder_name", - "card_cvc" - ], - "properties": { - "card_number": { - "type": "string", - "description": "The card number", - "example": "4242424242424242" - }, - "card_exp_month": { - "type": "string", - "description": "The card's expiry month", - "example": "24" - }, - "card_exp_year": { - "type": "string", - "description": "The card's expiry year", - "example": "24" - }, - "card_holder_name": { - "type": "string", - "description": "The card holder's name", - "example": "John Test" - }, - "card_cvc": { - "type": "string", - "description": "The CVC number for the card", - "example": "242" - }, - "card_issuer": { - "type": "string", - "description": "The name of the issuer of card", - "example": "chase", - "nullable": true - }, - "card_network": { - "allOf": [ - { - "$ref": "#/components/schemas/CardNetwork" - } - ], - "nullable": true - }, - "card_type": { - "type": "string", - "example": "CREDIT", - "nullable": true - }, - "card_issuing_country": { - "type": "string", - "example": "INDIA", - "nullable": true - }, - "bank_code": { - "type": "string", - "example": "JP_AMEX", - "nullable": true - }, - "nick_name": { - "type": "string", - "description": "The card holder's nick name", - "example": "John Test", - "nullable": true - } - } - }, - "CardDetail": { - "type": "object", - "required": [ - "card_number", - "card_exp_month", - "card_exp_year", - "card_holder_name" - ], - "properties": { - "card_number": { - "type": "string", - "description": "Card Number", - "example": "4111111145551142" - }, - "card_exp_month": { - "type": "string", - "description": "Card Expiry Month", - "example": "10" - }, - "card_exp_year": { - "type": "string", - "description": "Card Expiry Year", - "example": "25" - }, - "card_holder_name": { - "type": "string", - "description": "Card Holder Name", - "example": "John Doe" - }, - "nick_name": { - "type": "string", - "description": "Card Holder's Nick Name", - "example": "John Doe", - "nullable": true - }, - "card_issuing_country": { - "type": "string", - "description": "Card Issuing Country", - "nullable": true - }, - "card_network": { - "allOf": [ - { - "$ref": "#/components/schemas/CardNetwork" - } - ], - "nullable": true - }, - "card_issuer": { - "type": "string", - "description": "Issuer Bank for Card", - "nullable": true - }, - "card_type": { - "type": "string", - "description": "Card Type", - "nullable": true - } - }, - "additionalProperties": false - }, - "CardDetailFromLocker": { - "type": "object", - "required": [ - "saved_to_locker" - ], - "properties": { - "scheme": { - "type": "string", - "nullable": true - }, - "issuer_country": { - "type": "string", - "nullable": true - }, - "last4_digits": { - "type": "string", - "nullable": true - }, - "expiry_month": { - "type": "string", - "nullable": true - }, - "expiry_year": { - "type": "string", - "nullable": true - }, - "card_token": { - "type": "string", - "nullable": true - }, - "card_holder_name": { - "type": "string", - "nullable": true - }, - "card_fingerprint": { - "type": "string", - "nullable": true - }, - "nick_name": { - "type": "string", - "nullable": true - }, - "card_network": { - "allOf": [ - { - "$ref": "#/components/schemas/CardNetwork" - } - ], - "nullable": true - }, - "card_isin": { - "type": "string", - "nullable": true - }, - "card_issuer": { - "type": "string", - "nullable": true - }, - "card_type": { - "type": "string", - "nullable": true - }, - "saved_to_locker": { - "type": "boolean" - } - } - }, - "CardDetailUpdate": { - "type": "object", - "required": [ - "card_exp_month", - "card_exp_year", - "card_holder_name" - ], - "properties": { - "card_exp_month": { - "type": "string", - "description": "Card Expiry Month", - "example": "10" - }, - "card_exp_year": { - "type": "string", - "description": "Card Expiry Year", - "example": "25" - }, - "card_holder_name": { - "type": "string", - "description": "Card Holder Name", - "example": "John Doe" - }, - "nick_name": { - "type": "string", - "description": "Card Holder's Nick Name", - "example": "John Doe", + "description": "Card Holder's Nick Name", + "example": "John Doe", "nullable": true } }, @@ -9150,7 +8797,7 @@ }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", + "description": "Identifier for the profile, if not provided default will be chosen from merchant account", "maxLength": 64 }, "connector_account_details": { @@ -9349,7 +8996,7 @@ }, "connector_label": { "type": "string", - "description": "A unique label to identify the connector account created under a business profile", + "description": "A unique label to identify the connector account created under a profile", "example": "stripe_US_travel", "nullable": true }, @@ -9360,7 +9007,7 @@ }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", + "description": "Identifier for the profile, if not provided default will be chosen from merchant account", "maxLength": 64 }, "payment_methods_enabled": { @@ -9473,7 +9120,7 @@ }, "connector_label": { "type": "string", - "description": "A unique label to identify the connector account created under a business profile", + "description": "A unique label to identify the connector account created under a profile", "example": "stripe_US_travel", "nullable": true }, @@ -9484,7 +9131,7 @@ }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", + "description": "Identifier for the profile, if not provided default will be chosen from merchant account", "maxLength": 64 }, "connector_account_details": { @@ -16744,7 +16391,172 @@ "accommodation" ] }, - "ProfileDefaultRoutingConfig": { + "ProfileCreate": { + "type": "object", + "required": [ + "profile_name" + ], + "properties": { + "profile_name": { + "type": "string", + "description": "The name of profile", + "maxLength": 64 + }, + "return_url": { + "type": "string", + "description": "The URL to redirect after the completion of the operation", + "example": "https://www.example.com/success", + "nullable": true, + "maxLength": 255 + }, + "enable_payment_response_hash": { + "type": "boolean", + "description": "A boolean value to indicate if payment response hash needs to be enabled", + "default": true, + "example": true, + "nullable": true + }, + "payment_response_hash_key": { + "type": "string", + "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", + "nullable": true + }, + "redirect_to_merchant_with_http_post": { + "type": "boolean", + "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", + "default": false, + "example": true, + "nullable": true + }, + "webhook_details": { + "allOf": [ + { + "$ref": "#/components/schemas/WebhookDetails" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "description": "Metadata is useful for storing additional, unstructured information on an object.", + "nullable": true + }, + "order_fulfillment_time": { + "type": "integer", + "format": "int32", + "description": "Will be used to determine the time till which your payment will be active once the payment session starts", + "example": 900, + "nullable": true, + "minimum": 0 + }, + "order_fulfillment_time_origin": { + "allOf": [ + { + "$ref": "#/components/schemas/OrderFulfillmentTimeOrigin" + } + ], + "nullable": true + }, + "applepay_verified_domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified Apple Pay domains for a particular profile", + "nullable": true + }, + "session_expiry": { + "type": "integer", + "format": "int32", + "description": "Client Secret Default expiry for all payments created under this profile", + "example": 900, + "nullable": true, + "minimum": 0 + }, + "payment_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPaymentLinkConfig" + } + ], + "nullable": true + }, + "authentication_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationConnectorDetails" + } + ], + "nullable": true + }, + "use_billing_as_payment_method_billing": { + "type": "boolean", + "description": "Whether to use the billing details passed when creating the intent as payment method billing", + "nullable": true + }, + "collect_shipping_details_from_wallet_connector_if_required": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "collect_billing_details_from_wallet_connector_if_required": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_shipping_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_billing_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "is_connector_agnostic_mit_enabled": { + "type": "boolean", + "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "nullable": true + }, + "payout_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + } + ], + "nullable": true + }, + "outgoing_webhook_custom_http_headers": { + "type": "object", + "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request. It is recommended not to use more than four key-value pairs.", + "nullable": true + }, + "tax_connector_id": { + "type": "string", + "description": "Merchant Connector id to be stored for tax_calculator connector", + "nullable": true + }, + "is_tax_connector_enabled": { + "type": "boolean", + "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." + } + }, + "additionalProperties": false + }, + "ProfileDefaultRoutingConfig": { "type": "object", "required": [ "profile_id", @@ -16762,6 +16574,194 @@ } } }, + "ProfileResponse": { + "type": "object", + "required": [ + "merchant_id", + "id", + "profile_name", + "enable_payment_response_hash", + "redirect_to_merchant_with_http_post", + "is_tax_connector_enabled", + "is_network_tokenization_enabled" + ], + "properties": { + "merchant_id": { + "type": "string", + "description": "The identifier for Merchant Account", + "example": "y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64 + }, + "id": { + "type": "string", + "description": "The identifier for profile. This must be used for creating merchant accounts, payments and payouts", + "example": "pro_abcdefghijklmnopqrstuvwxyz", + "maxLength": 64 + }, + "profile_name": { + "type": "string", + "description": "Name of the profile", + "maxLength": 64 + }, + "return_url": { + "type": "string", + "description": "The URL to redirect after the completion of the operation", + "example": "https://www.example.com/success", + "nullable": true, + "maxLength": 255 + }, + "enable_payment_response_hash": { + "type": "boolean", + "description": "A boolean value to indicate if payment response hash needs to be enabled", + "default": true, + "example": true + }, + "payment_response_hash_key": { + "type": "string", + "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", + "nullable": true + }, + "redirect_to_merchant_with_http_post": { + "type": "boolean", + "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", + "default": false, + "example": true + }, + "webhook_details": { + "allOf": [ + { + "$ref": "#/components/schemas/WebhookDetails" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "description": "Metadata is useful for storing additional, unstructured information on an object.", + "nullable": true + }, + "applepay_verified_domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified Apple Pay domains for a particular profile", + "nullable": true + }, + "session_expiry": { + "type": "integer", + "format": "int64", + "description": "Client Secret Default expiry for all payments created under this profile", + "example": 900, + "nullable": true + }, + "payment_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPaymentLinkConfig" + } + ], + "nullable": true + }, + "authentication_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationConnectorDetails" + } + ], + "nullable": true + }, + "use_billing_as_payment_method_billing": { + "type": "boolean", + "nullable": true + }, + "extended_card_info_config": { + "allOf": [ + { + "$ref": "#/components/schemas/ExtendedCardInfoConfig" + } + ], + "nullable": true + }, + "collect_shipping_details_from_wallet_connector_if_required": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "collect_billing_details_from_wallet_connector_if_required": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_shipping_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_billing_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "is_connector_agnostic_mit_enabled": { + "type": "boolean", + "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "nullable": true + }, + "payout_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + } + ], + "nullable": true + }, + "outgoing_webhook_custom_http_headers": { + "type": "object", + "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request.", + "nullable": true + }, + "order_fulfillment_time": { + "type": "integer", + "format": "int32", + "description": "Will be used to determine the time till which your payment will be active once the payment session starts", + "example": 900, + "nullable": true, + "minimum": 0 + }, + "order_fulfillment_time_origin": { + "allOf": [ + { + "$ref": "#/components/schemas/OrderFulfillmentTimeOrigin" + } + ], + "nullable": true + }, + "tax_connector_id": { + "type": "string", + "description": "Merchant Connector id to be stored for tax_calculator connector", + "nullable": true + }, + "is_tax_connector_enabled": { + "type": "boolean", + "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", + "default": false, + "example": false + } + } + }, "ProgramConnectorSelection": { "type": "object", "description": "The program, having a default connector selection and\na bunch of rules. Also can hold arbitrary metadata.", @@ -19612,8 +19612,8 @@ "description": "Create and manage merchant accounts" }, { - "name": "Business Profile", - "description": "Create and manage business profiles" + "name": "Profile", + "description": "Create and manage profiles" }, { "name": "Merchant Connector Account", diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 2d0a7147570..4934b4fb345 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -1558,7 +1558,7 @@ "connector_type": "payment_processor" } }, - "Create a merchant connector account under a specific business profile": { + "Create a merchant connector account under a specific profile": { "value": { "connector_account_details": { "api_key": "{{adyen-api-key}}", @@ -2912,11 +2912,11 @@ "/account/{account_id}/business_profile": { "post": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Create", - "description": "Creates a new *business profile* for a merchant", - "operationId": "Create A Business Profile", + "summary": "Profile - Create", + "description": "Creates a new *profile* for a merchant", + "operationId": "Create A Profile", "parameters": [ { "name": "account_id", @@ -2932,13 +2932,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileCreate" + "$ref": "#/components/schemas/ProfileCreate" }, "examples": { - "Create a business profile with minimal fields": { + "Create a profile with minimal fields": { "value": {} }, - "Create a business profile with profile name": { + "Create a profile with profile name": { "value": { "profile_name": "shoe_business" } @@ -2950,11 +2950,11 @@ }, "responses": { "200": { - "description": "Business Account Created", + "description": "Profile Created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -2971,11 +2971,11 @@ }, "get": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - List", - "description": "Lists all the *business profiles* under a merchant", - "operationId": "List Business Profiles", + "summary": "Profile - List", + "description": "Lists all the *profiles* under a merchant", + "operationId": "List Profiles", "parameters": [ { "name": "account_id", @@ -2989,13 +2989,13 @@ ], "responses": { "200": { - "description": "Business profiles Retrieved", + "description": "Profiles Retrieved", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -3012,11 +3012,11 @@ "/account/{account_id}/business_profile/{profile_id}": { "get": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Retrieve", - "description": "Retrieve existing *business profile*", - "operationId": "Retrieve a Business Profile", + "summary": "Profile - Retrieve", + "description": "Retrieve existing *profile*", + "operationId": "Retrieve a Profile", "parameters": [ { "name": "account_id", @@ -3030,7 +3030,7 @@ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -3039,11 +3039,11 @@ ], "responses": { "200": { - "description": "Business Profile Updated", + "description": "Profile Updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -3060,11 +3060,11 @@ }, "post": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Update", - "description": "Update the *business profile*", - "operationId": "Update a Business Profile", + "summary": "Profile - Update", + "description": "Update the *profile*", + "operationId": "Update a Profile", "parameters": [ { "name": "account_id", @@ -3078,7 +3078,7 @@ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -3089,10 +3089,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileCreate" + "$ref": "#/components/schemas/ProfileCreate" }, "examples": { - "Update business profile with profile name fields": { + "Update profile with profile name fields": { "value": { "profile_name": "shoe_business" } @@ -3104,11 +3104,11 @@ }, "responses": { "200": { - "description": "Business Profile Updated", + "description": "Profile Updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProfileResponse" + "$ref": "#/components/schemas/ProfileResponse" } } } @@ -3125,11 +3125,11 @@ }, "delete": { "tags": [ - "Business Profile" + "Profile" ], - "summary": "Business Profile - Delete", - "description": "Delete the *business profile*", - "operationId": "Delete the Business Profile", + "summary": "Profile - Delete", + "description": "Delete the *profile*", + "operationId": "Delete the Profile", "parameters": [ { "name": "account_id", @@ -3143,7 +3143,7 @@ { "name": "profile_id", "in": "path", - "description": "The unique identifier for the business profile", + "description": "The unique identifier for the profile", "required": true, "schema": { "type": "string" @@ -3152,7 +3152,7 @@ ], "responses": { "200": { - "description": "Business profiles Deleted", + "description": "Profiles Deleted", "content": { "text/plain": { "schema": { @@ -4847,8 +4847,8 @@ "Event" ], "summary": "Events - List", - "description": "List all Events associated with a Merchant Account or Business Profile.", - "operationId": "List all Events associated with a Merchant Account or Business Profile", + "description": "List all Events associated with a Merchant Account or Profile.", + "operationId": "List all Events associated with a Merchant Account or Profile", "parameters": [ { "name": "merchant_id", @@ -4916,7 +4916,7 @@ { "name": "profile_id", "in": "query", - "description": "Only include Events associated with the Business Profile identified by the specified Business Profile ID.", + "description": "Only include Events associated with the Profile identified by the specified Profile ID.", "required": false, "schema": { "type": "string", @@ -7794,694 +7794,324 @@ } ] }, - "BusinessProfileCreate": { + "CaptureMethod": { + "type": "string", + "description": "Default value if not passed is set to 'automatic' which results in Auth and Capture in one single API request. Pass 'manual' or 'manual_multiple' in case you want do a separate Auth and Capture by first authorizing and placing a hold on your customer's funds so that you can use the Payments/Capture endpoint later to capture the authorized amount. Pass 'manual' if you want to only capture the amount later once or 'manual_multiple' if you want to capture the funds multiple times later. Both 'manual' and 'manual_multiple' are only supported by a specific list of processors", + "enum": [ + "automatic", + "manual", + "manual_multiple", + "scheduled" + ] + }, + "CaptureResponse": { "type": "object", + "required": [ + "capture_id", + "status", + "amount", + "connector", + "authorized_attempt_id", + "capture_sequence" + ], "properties": { - "profile_name": { - "type": "string", - "description": "The name of business profile", - "nullable": true, - "maxLength": 64 - }, - "return_url": { + "capture_id": { "type": "string", - "description": "The URL to redirect after the completion of the operation", - "example": "https://www.example.com/success", - "nullable": true, - "maxLength": 255 - }, - "enable_payment_response_hash": { - "type": "boolean", - "description": "A boolean value to indicate if payment response hash needs to be enabled", - "default": true, - "example": true, - "nullable": true + "description": "Unique identifier for the capture" }, - "payment_response_hash_key": { - "type": "string", - "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", - "nullable": true + "status": { + "$ref": "#/components/schemas/CaptureStatus" }, - "redirect_to_merchant_with_http_post": { - "type": "boolean", - "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", - "default": false, - "example": true, - "nullable": true + "amount": { + "type": "integer", + "format": "int64", + "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", + "example": 6540 }, - "webhook_details": { + "currency": { "allOf": [ { - "$ref": "#/components/schemas/WebhookDetails" + "$ref": "#/components/schemas/Currency" } ], "nullable": true }, - "metadata": { - "type": "object", - "description": "Metadata is useful for storing additional, unstructured information on an object.", - "nullable": true - }, - "routing_algorithm": { - "type": "object", - "description": "The routing algorithm to be used for routing payments to desired connectors", - "nullable": true - }, - "intent_fulfillment_time": { - "type": "integer", - "format": "int32", - "description": "Will be used to determine the time till which your payment will be active once the payment session starts", - "example": 900, - "nullable": true, - "minimum": 0 - }, - "frm_routing_algorithm": { - "type": "object", - "description": "The frm routing algorithm to be used for routing payments to desired FRM's", - "nullable": true + "connector": { + "type": "string", + "description": "The connector used for the payment" }, - "payout_routing_algorithm": { - "allOf": [ - { - "$ref": "#/components/schemas/RoutingAlgorithm" - } - ], - "nullable": true + "authorized_attempt_id": { + "type": "string", + "description": "Unique identifier for the parent attempt on which this capture is made" }, - "applepay_verified_domains": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified Apple Pay domains for a particular profile", + "connector_capture_id": { + "type": "string", + "description": "A unique identifier for this capture provided by the connector", "nullable": true }, - "session_expiry": { + "capture_sequence": { "type": "integer", "format": "int32", - "description": "Client Secret Default expiry for all payments created under this business profile", - "example": 900, - "nullable": true, - "minimum": 0 + "description": "Sequence number of this capture, in the series of captures made for the parent attempt" }, - "payment_link_config": { - "allOf": [ - { - "$ref": "#/components/schemas/BusinessPaymentLinkConfig" - } - ], + "error_message": { + "type": "string", + "description": "If there was an error while calling the connector the error message is received here", "nullable": true }, - "authentication_connector_details": { - "allOf": [ - { - "$ref": "#/components/schemas/AuthenticationConnectorDetails" - } - ], + "error_code": { + "type": "string", + "description": "If there was an error while calling the connectors the code is received here", "nullable": true }, - "use_billing_as_payment_method_billing": { - "type": "boolean", - "description": "Whether to use the billing details passed when creating the intent as payment method billing", + "error_reason": { + "type": "string", + "description": "If there was an error while calling the connectors the reason is received here", "nullable": true }, - "collect_shipping_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, + "reference_id": { + "type": "string", + "description": "Reference to the capture at connector side", "nullable": true + } + } + }, + "CaptureStatus": { + "type": "string", + "enum": [ + "started", + "charged", + "pending", + "failed" + ] + }, + "Card": { + "type": "object", + "required": [ + "card_number", + "card_exp_month", + "card_exp_year", + "card_holder_name", + "card_cvc" + ], + "properties": { + "card_number": { + "type": "string", + "description": "The card number", + "example": "4242424242424242" }, - "collect_billing_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, - "nullable": true + "card_exp_month": { + "type": "string", + "description": "The card's expiry month", + "example": "24" }, - "always_collect_shipping_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, - "nullable": true + "card_exp_year": { + "type": "string", + "description": "The card's expiry year", + "example": "24" }, - "always_collect_billing_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, - "nullable": true + "card_holder_name": { + "type": "string", + "description": "The card holder's name", + "example": "John Test" }, - "is_connector_agnostic_mit_enabled": { - "type": "boolean", - "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "card_cvc": { + "type": "string", + "description": "The CVC number for the card", + "example": "242" + }, + "card_issuer": { + "type": "string", + "description": "The name of the issuer of card", + "example": "chase", "nullable": true }, - "payout_link_config": { + "card_network": { "allOf": [ { - "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + "$ref": "#/components/schemas/CardNetwork" } ], "nullable": true }, - "outgoing_webhook_custom_http_headers": { - "type": "object", - "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request. It is recommended not to use more than four key-value pairs.", + "card_type": { + "type": "string", + "example": "CREDIT", "nullable": true }, - "tax_connector_id": { + "card_issuing_country": { "type": "string", - "description": "Merchant Connector id to be stored for tax_calculator connector", + "example": "INDIA", "nullable": true }, - "is_tax_connector_enabled": { - "type": "boolean", - "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + "bank_code": { + "type": "string", + "example": "JP_AMEX", + "nullable": true }, - "is_network_tokenization_enabled": { - "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." + "nick_name": { + "type": "string", + "description": "The card holder's nick name", + "example": "John Test", + "nullable": true } - }, - "additionalProperties": false + } }, - "BusinessProfileResponse": { + "CardDetail": { "type": "object", "required": [ - "merchant_id", - "profile_id", - "profile_name", - "enable_payment_response_hash", - "redirect_to_merchant_with_http_post", - "is_tax_connector_enabled", - "is_network_tokenization_enabled" + "card_number", + "card_exp_month", + "card_exp_year", + "card_holder_name" ], "properties": { - "merchant_id": { + "card_number": { "type": "string", - "description": "The identifier for Merchant Account", - "example": "y3oqhf46pyzuxjbcn2giaqnb44", - "maxLength": 64 + "description": "Card Number", + "example": "4111111145551142" }, - "profile_id": { + "card_exp_month": { "type": "string", - "description": "The identifier for business profile. This must be used for creating merchant accounts, payments and payouts", - "example": "pro_abcdefghijklmnopqrstuvwxyz", - "maxLength": 64 + "description": "Card Expiry Month", + "example": "10" }, - "profile_name": { + "card_exp_year": { "type": "string", - "description": "Name of the business profile", - "maxLength": 64 + "description": "Card Expiry Year", + "example": "25" }, - "return_url": { + "card_holder_name": { "type": "string", - "description": "The URL to redirect after the completion of the operation", - "example": "https://www.example.com/success", - "nullable": true, - "maxLength": 255 - }, - "enable_payment_response_hash": { - "type": "boolean", - "description": "A boolean value to indicate if payment response hash needs to be enabled", - "default": true, - "example": true + "description": "Card Holder Name", + "example": "John Doe" }, - "payment_response_hash_key": { + "nick_name": { "type": "string", - "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", - "nullable": true - }, - "redirect_to_merchant_with_http_post": { - "type": "boolean", - "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", - "default": false, - "example": true - }, - "webhook_details": { - "allOf": [ - { - "$ref": "#/components/schemas/WebhookDetails" - } - ], - "nullable": true - }, - "metadata": { - "type": "object", - "description": "Metadata is useful for storing additional, unstructured information on an object.", - "nullable": true - }, - "routing_algorithm": { - "type": "object", - "description": "The routing algorithm to be used for routing payments to desired connectors", - "nullable": true - }, - "intent_fulfillment_time": { - "type": "integer", - "format": "int64", - "description": "Will be used to determine the time till which your payment will be active once the payment session starts", - "example": 900, + "description": "Card Holder's Nick Name", + "example": "John Doe", "nullable": true }, - "frm_routing_algorithm": { - "type": "object", - "description": "The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'", + "card_issuing_country": { + "type": "string", + "description": "Card Issuing Country", "nullable": true }, - "payout_routing_algorithm": { + "card_network": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/CardNetwork" } ], "nullable": true }, - "applepay_verified_domains": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Verified Apple Pay domains for a particular profile", + "card_issuer": { + "type": "string", + "description": "Issuer Bank for Card", "nullable": true }, - "session_expiry": { - "type": "integer", - "format": "int64", - "description": "Client Secret Default expiry for all payments created under this business profile", - "example": 900, + "card_type": { + "type": "string", + "description": "Card Type", "nullable": true - }, - "payment_link_config": { - "allOf": [ - { - "$ref": "#/components/schemas/BusinessPaymentLinkConfig" - } - ], + } + }, + "additionalProperties": false + }, + "CardDetailFromLocker": { + "type": "object", + "required": [ + "saved_to_locker" + ], + "properties": { + "scheme": { + "type": "string", "nullable": true }, - "authentication_connector_details": { - "allOf": [ - { - "$ref": "#/components/schemas/AuthenticationConnectorDetails" - } - ], + "issuer_country": { + "type": "string", "nullable": true }, - "use_billing_as_payment_method_billing": { - "type": "boolean", + "last4_digits": { + "type": "string", "nullable": true }, - "extended_card_info_config": { - "allOf": [ - { - "$ref": "#/components/schemas/ExtendedCardInfoConfig" - } - ], + "expiry_month": { + "type": "string", "nullable": true }, - "collect_shipping_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, + "expiry_year": { + "type": "string", "nullable": true }, - "collect_billing_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", - "default": false, - "example": false, + "card_token": { + "type": "string", "nullable": true }, - "always_collect_shipping_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, + "card_holder_name": { + "type": "string", "nullable": true }, - "always_collect_billing_details_from_wallet_connector": { - "type": "boolean", - "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", - "default": false, - "example": false, + "card_fingerprint": { + "type": "string", "nullable": true }, - "is_connector_agnostic_mit_enabled": { - "type": "boolean", - "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "nick_name": { + "type": "string", "nullable": true }, - "payout_link_config": { + "card_network": { "allOf": [ { - "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + "$ref": "#/components/schemas/CardNetwork" } ], "nullable": true }, - "outgoing_webhook_custom_http_headers": { - "type": "object", - "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request.", + "card_isin": { + "type": "string", "nullable": true }, - "tax_connector_id": { + "card_issuer": { "type": "string", - "description": "Merchant Connector id to be stored for tax_calculator connector", "nullable": true }, - "is_tax_connector_enabled": { - "type": "boolean", - "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + "card_type": { + "type": "string", + "nullable": true }, - "is_network_tokenization_enabled": { - "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", - "default": false, - "example": false + "saved_to_locker": { + "type": "boolean" } } }, - "CaptureMethod": { - "type": "string", - "description": "Default value if not passed is set to 'automatic' which results in Auth and Capture in one single API request. Pass 'manual' or 'manual_multiple' in case you want do a separate Auth and Capture by first authorizing and placing a hold on your customer's funds so that you can use the Payments/Capture endpoint later to capture the authorized amount. Pass 'manual' if you want to only capture the amount later once or 'manual_multiple' if you want to capture the funds multiple times later. Both 'manual' and 'manual_multiple' are only supported by a specific list of processors", - "enum": [ - "automatic", - "manual", - "manual_multiple", - "scheduled" - ] - }, - "CaptureResponse": { + "CardDetailUpdate": { "type": "object", "required": [ - "capture_id", - "status", - "amount", - "connector", - "authorized_attempt_id", - "capture_sequence" + "card_exp_month", + "card_exp_year", + "card_holder_name" ], "properties": { - "capture_id": { - "type": "string", - "description": "Unique identifier for the capture" - }, - "status": { - "$ref": "#/components/schemas/CaptureStatus" - }, - "amount": { - "type": "integer", - "format": "int64", - "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", - "example": 6540 - }, - "currency": { - "allOf": [ - { - "$ref": "#/components/schemas/Currency" - } - ], - "nullable": true - }, - "connector": { + "card_exp_month": { "type": "string", - "description": "The connector used for the payment" + "description": "Card Expiry Month", + "example": "10" }, - "authorized_attempt_id": { + "card_exp_year": { "type": "string", - "description": "Unique identifier for the parent attempt on which this capture is made" + "description": "Card Expiry Year", + "example": "25" }, - "connector_capture_id": { + "card_holder_name": { "type": "string", - "description": "A unique identifier for this capture provided by the connector", - "nullable": true - }, - "capture_sequence": { - "type": "integer", - "format": "int32", - "description": "Sequence number of this capture, in the series of captures made for the parent attempt" + "description": "Card Holder Name", + "example": "John Doe" }, - "error_message": { + "nick_name": { "type": "string", - "description": "If there was an error while calling the connector the error message is received here", - "nullable": true - }, - "error_code": { - "type": "string", - "description": "If there was an error while calling the connectors the code is received here", - "nullable": true - }, - "error_reason": { - "type": "string", - "description": "If there was an error while calling the connectors the reason is received here", - "nullable": true - }, - "reference_id": { - "type": "string", - "description": "Reference to the capture at connector side", - "nullable": true - } - } - }, - "CaptureStatus": { - "type": "string", - "enum": [ - "started", - "charged", - "pending", - "failed" - ] - }, - "Card": { - "type": "object", - "required": [ - "card_number", - "card_exp_month", - "card_exp_year", - "card_holder_name", - "card_cvc" - ], - "properties": { - "card_number": { - "type": "string", - "description": "The card number", - "example": "4242424242424242" - }, - "card_exp_month": { - "type": "string", - "description": "The card's expiry month", - "example": "24" - }, - "card_exp_year": { - "type": "string", - "description": "The card's expiry year", - "example": "24" - }, - "card_holder_name": { - "type": "string", - "description": "The card holder's name", - "example": "John Test" - }, - "card_cvc": { - "type": "string", - "description": "The CVC number for the card", - "example": "242" - }, - "card_issuer": { - "type": "string", - "description": "The name of the issuer of card", - "example": "chase", - "nullable": true - }, - "card_network": { - "allOf": [ - { - "$ref": "#/components/schemas/CardNetwork" - } - ], - "nullable": true - }, - "card_type": { - "type": "string", - "example": "CREDIT", - "nullable": true - }, - "card_issuing_country": { - "type": "string", - "example": "INDIA", - "nullable": true - }, - "bank_code": { - "type": "string", - "example": "JP_AMEX", - "nullable": true - }, - "nick_name": { - "type": "string", - "description": "The card holder's nick name", - "example": "John Test", - "nullable": true - } - } - }, - "CardDetail": { - "type": "object", - "required": [ - "card_number", - "card_exp_month", - "card_exp_year", - "card_holder_name" - ], - "properties": { - "card_number": { - "type": "string", - "description": "Card Number", - "example": "4111111145551142" - }, - "card_exp_month": { - "type": "string", - "description": "Card Expiry Month", - "example": "10" - }, - "card_exp_year": { - "type": "string", - "description": "Card Expiry Year", - "example": "25" - }, - "card_holder_name": { - "type": "string", - "description": "Card Holder Name", - "example": "John Doe" - }, - "nick_name": { - "type": "string", - "description": "Card Holder's Nick Name", - "example": "John Doe", - "nullable": true - }, - "card_issuing_country": { - "type": "string", - "description": "Card Issuing Country", - "nullable": true - }, - "card_network": { - "allOf": [ - { - "$ref": "#/components/schemas/CardNetwork" - } - ], - "nullable": true - }, - "card_issuer": { - "type": "string", - "description": "Issuer Bank for Card", - "nullable": true - }, - "card_type": { - "type": "string", - "description": "Card Type", - "nullable": true - } - }, - "additionalProperties": false - }, - "CardDetailFromLocker": { - "type": "object", - "required": [ - "saved_to_locker" - ], - "properties": { - "scheme": { - "type": "string", - "nullable": true - }, - "issuer_country": { - "type": "string", - "nullable": true - }, - "last4_digits": { - "type": "string", - "nullable": true - }, - "expiry_month": { - "type": "string", - "nullable": true - }, - "expiry_year": { - "type": "string", - "nullable": true - }, - "card_token": { - "type": "string", - "nullable": true - }, - "card_holder_name": { - "type": "string", - "nullable": true - }, - "card_fingerprint": { - "type": "string", - "nullable": true - }, - "nick_name": { - "type": "string", - "nullable": true - }, - "card_network": { - "allOf": [ - { - "$ref": "#/components/schemas/CardNetwork" - } - ], - "nullable": true - }, - "card_isin": { - "type": "string", - "nullable": true - }, - "card_issuer": { - "type": "string", - "nullable": true - }, - "card_type": { - "type": "string", - "nullable": true - }, - "saved_to_locker": { - "type": "boolean" - } - } - }, - "CardDetailUpdate": { - "type": "object", - "required": [ - "card_exp_month", - "card_exp_year", - "card_holder_name" - ], - "properties": { - "card_exp_month": { - "type": "string", - "description": "Card Expiry Month", - "example": "10" - }, - "card_exp_year": { - "type": "string", - "description": "Card Expiry Year", - "example": "25" - }, - "card_holder_name": { - "type": "string", - "description": "Card Holder Name", - "example": "John Doe" - }, - "nick_name": { - "type": "string", - "description": "Card Holder's Nick Name", - "example": "John Doe", + "description": "Card Holder's Nick Name", + "example": "John Doe", "nullable": true } }, @@ -12715,7 +12345,7 @@ }, "default_profile": { "type": "string", - "description": "The default business profile that must be used for creating merchant accounts and payments", + "description": "The default profile that must be used for creating merchant accounts and payments", "nullable": true, "maxLength": 64 }, @@ -12846,7 +12476,7 @@ }, "default_profile": { "type": "string", - "description": "The default business profile that must be used for creating merchant accounts and payments", + "description": "The default profile that must be used for creating merchant accounts and payments", "nullable": true, "maxLength": 64 }, @@ -12883,7 +12513,7 @@ }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", + "description": "Identifier for the profile, if not provided default will be chosen from merchant account", "nullable": true, "maxLength": 64 }, @@ -13118,7 +12748,7 @@ }, "connector_label": { "type": "string", - "description": "A unique label to identify the connector account created under a business profile", + "description": "A unique label to identify the connector account created under a profile", "example": "stripe_US_travel", "nullable": true }, @@ -13129,7 +12759,7 @@ }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", + "description": "Identifier for the profile, if not provided default will be chosen from merchant account", "maxLength": 64 }, "payment_methods_enabled": { @@ -13269,7 +12899,7 @@ }, "connector_label": { "type": "string", - "description": "A unique label to identify the connector account created under a business profile", + "description": "A unique label to identify the connector account created under a profile", "example": "stripe_US_travel", "nullable": true }, @@ -13280,7 +12910,7 @@ }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", + "description": "Identifier for the profile, if not provided default will be chosen from merchant account", "maxLength": 64 }, "connector_account_details": { @@ -20998,7 +20628,180 @@ "accommodation" ] }, - "ProfileDefaultRoutingConfig": { + "ProfileCreate": { + "type": "object", + "properties": { + "profile_name": { + "type": "string", + "description": "The name of profile", + "nullable": true, + "maxLength": 64 + }, + "return_url": { + "type": "string", + "description": "The URL to redirect after the completion of the operation", + "example": "https://www.example.com/success", + "nullable": true, + "maxLength": 255 + }, + "enable_payment_response_hash": { + "type": "boolean", + "description": "A boolean value to indicate if payment response hash needs to be enabled", + "default": true, + "example": true, + "nullable": true + }, + "payment_response_hash_key": { + "type": "string", + "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", + "nullable": true + }, + "redirect_to_merchant_with_http_post": { + "type": "boolean", + "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", + "default": false, + "example": true, + "nullable": true + }, + "webhook_details": { + "allOf": [ + { + "$ref": "#/components/schemas/WebhookDetails" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "description": "Metadata is useful for storing additional, unstructured information on an object.", + "nullable": true + }, + "routing_algorithm": { + "type": "object", + "description": "The routing algorithm to be used for routing payments to desired connectors", + "nullable": true + }, + "intent_fulfillment_time": { + "type": "integer", + "format": "int32", + "description": "Will be used to determine the time till which your payment will be active once the payment session starts", + "example": 900, + "nullable": true, + "minimum": 0 + }, + "frm_routing_algorithm": { + "type": "object", + "description": "The frm routing algorithm to be used for routing payments to desired FRM's", + "nullable": true + }, + "payout_routing_algorithm": { + "allOf": [ + { + "$ref": "#/components/schemas/RoutingAlgorithm" + } + ], + "nullable": true + }, + "applepay_verified_domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified Apple Pay domains for a particular profile", + "nullable": true + }, + "session_expiry": { + "type": "integer", + "format": "int32", + "description": "Client Secret Default expiry for all payments created under this profile", + "example": 900, + "nullable": true, + "minimum": 0 + }, + "payment_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPaymentLinkConfig" + } + ], + "nullable": true + }, + "authentication_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationConnectorDetails" + } + ], + "nullable": true + }, + "use_billing_as_payment_method_billing": { + "type": "boolean", + "description": "Whether to use the billing details passed when creating the intent as payment method billing", + "nullable": true + }, + "collect_shipping_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "collect_billing_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_shipping_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_billing_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "is_connector_agnostic_mit_enabled": { + "type": "boolean", + "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "nullable": true + }, + "payout_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + } + ], + "nullable": true + }, + "outgoing_webhook_custom_http_headers": { + "type": "object", + "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request. It is recommended not to use more than four key-value pairs.", + "nullable": true + }, + "tax_connector_id": { + "type": "string", + "description": "Merchant Connector id to be stored for tax_calculator connector", + "nullable": true + }, + "is_tax_connector_enabled": { + "type": "boolean", + "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." + } + }, + "additionalProperties": false + }, + "ProfileDefaultRoutingConfig": { "type": "object", "required": [ "profile_id", @@ -21016,6 +20819,203 @@ } } }, + "ProfileResponse": { + "type": "object", + "required": [ + "merchant_id", + "profile_id", + "profile_name", + "enable_payment_response_hash", + "redirect_to_merchant_with_http_post", + "is_tax_connector_enabled", + "is_network_tokenization_enabled" + ], + "properties": { + "merchant_id": { + "type": "string", + "description": "The identifier for Merchant Account", + "example": "y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64 + }, + "profile_id": { + "type": "string", + "description": "The identifier for profile. This must be used for creating merchant accounts, payments and payouts", + "example": "pro_abcdefghijklmnopqrstuvwxyz", + "maxLength": 64 + }, + "profile_name": { + "type": "string", + "description": "Name of the profile", + "maxLength": 64 + }, + "return_url": { + "type": "string", + "description": "The URL to redirect after the completion of the operation", + "example": "https://www.example.com/success", + "nullable": true, + "maxLength": 255 + }, + "enable_payment_response_hash": { + "type": "boolean", + "description": "A boolean value to indicate if payment response hash needs to be enabled", + "default": true, + "example": true + }, + "payment_response_hash_key": { + "type": "string", + "description": "Refers to the hash key used for calculating the signature for webhooks and redirect response. If the value is not provided, a value is automatically generated.", + "nullable": true + }, + "redirect_to_merchant_with_http_post": { + "type": "boolean", + "description": "A boolean value to indicate if redirect to merchant with http post needs to be enabled", + "default": false, + "example": true + }, + "webhook_details": { + "allOf": [ + { + "$ref": "#/components/schemas/WebhookDetails" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "description": "Metadata is useful for storing additional, unstructured information on an object.", + "nullable": true + }, + "routing_algorithm": { + "type": "object", + "description": "The routing algorithm to be used for routing payments to desired connectors", + "nullable": true + }, + "intent_fulfillment_time": { + "type": "integer", + "format": "int64", + "description": "Will be used to determine the time till which your payment will be active once the payment session starts", + "example": 900, + "nullable": true + }, + "frm_routing_algorithm": { + "type": "object", + "description": "The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom'", + "nullable": true + }, + "payout_routing_algorithm": { + "allOf": [ + { + "$ref": "#/components/schemas/RoutingAlgorithm" + } + ], + "nullable": true + }, + "applepay_verified_domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Verified Apple Pay domains for a particular profile", + "nullable": true + }, + "session_expiry": { + "type": "integer", + "format": "int64", + "description": "Client Secret Default expiry for all payments created under this profile", + "example": 900, + "nullable": true + }, + "payment_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPaymentLinkConfig" + } + ], + "nullable": true + }, + "authentication_connector_details": { + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationConnectorDetails" + } + ], + "nullable": true + }, + "use_billing_as_payment_method_billing": { + "type": "boolean", + "nullable": true + }, + "extended_card_info_config": { + "allOf": [ + { + "$ref": "#/components/schemas/ExtendedCardInfoConfig" + } + ], + "nullable": true + }, + "collect_shipping_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "collect_billing_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector only if it is required field for connector (Eg. Apple Pay, Google Pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_shipping_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer shipping details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "always_collect_billing_details_from_wallet_connector": { + "type": "boolean", + "description": "A boolean value to indicate if customer billing details needs to be collected from wallet\nconnector irrespective of connector required fields (Eg. Apple pay, Google pay etc)", + "default": false, + "example": false, + "nullable": true + }, + "is_connector_agnostic_mit_enabled": { + "type": "boolean", + "description": "Indicates if the MIT (merchant initiated transaction) payments can be made connector\nagnostic, i.e., MITs may be processed through different connector than CIT (customer\ninitiated transaction) based on the routing rules.\nIf set to `false`, MIT will go through the same connector as the CIT.", + "nullable": true + }, + "payout_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/BusinessPayoutLinkConfig" + } + ], + "nullable": true + }, + "outgoing_webhook_custom_http_headers": { + "type": "object", + "description": "These key-value pairs are sent as additional custom headers in the outgoing webhook request.", + "nullable": true + }, + "tax_connector_id": { + "type": "string", + "description": "Merchant Connector id to be stored for tax_calculator connector", + "nullable": true + }, + "is_tax_connector_enabled": { + "type": "boolean", + "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", + "default": false, + "example": false + } + } + }, "ProgramConnectorSelection": { "type": "object", "description": "The program, having a default connector selection and\na bunch of rules. Also can hold arbitrary metadata.", @@ -23953,8 +23953,8 @@ "description": "Create and manage merchant accounts" }, { - "name": "Business Profile", - "description": "Create and manage business profiles" + "name": "Profile", + "description": "Create and manage profiles" }, { "name": "Merchant Connector Account", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 3b9a8e1c983..5a9beb565d4 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -210,6 +210,15 @@ impl MerchantAccountCreate { .transpose() } + pub fn get_metadata_as_secret( + &self, + ) -> CustomResult, errors::ParsingError> { + self.metadata + .as_ref() + .map(|metadata| metadata.encode_to_value().map(Secret::new)) + .transpose() + } + pub fn get_primary_details_as_value( &self, ) -> CustomResult { @@ -304,7 +313,7 @@ pub struct MerchantAccountUpdate { #[schema(value_type = Option,example = json!({"type": "single", "data": "signifyd"}))] pub frm_routing_algorithm: Option, - /// The default business profile that must be used for creating merchant accounts and payments + /// The default profile that must be used for creating merchant accounts and payments #[schema(max_length = 64, value_type = Option)] pub default_profile: Option, @@ -493,7 +502,7 @@ pub struct MerchantAccountResponse { /// A boolean value to indicate if the merchant has recon service is enabled or not, by default value is false pub is_recon_enabled: bool, - /// The default business profile that must be used for creating merchant accounts and payments + /// The default profile that must be used for creating merchant accounts and payments #[schema(max_length = 64, value_type = Option)] pub default_profile: Option, @@ -665,7 +674,7 @@ pub struct MerchantConnectorCreate { #[schema(example = "stripe_US_travel")] pub connector_label: Option, - /// Identifier for the business profile, if not provided default will be chosen from merchant account + /// Identifier for the profile, if not provided default will be chosen from merchant account #[schema(max_length = 64, value_type = String)] pub profile_id: id_type::ProfileId, @@ -785,7 +794,7 @@ pub struct MerchantConnectorCreate { #[schema(example = "stripe_US_travel")] pub connector_label: Option, - /// Identifier for the business profile, if not provided default will be chosen from merchant account + /// Identifier for the profile, if not provided default will be chosen from merchant account #[schema(max_length = 64, value_type = Option)] pub profile_id: Option, @@ -1013,7 +1022,7 @@ pub struct MerchantConnectorResponse { #[schema(value_type = Connector, example = "stripe")] pub connector_name: String, - /// A unique label to identify the connector account created under a business profile + /// A unique label to identify the connector account created under a profile #[schema(example = "stripe_US_travel")] pub connector_label: Option, @@ -1021,7 +1030,7 @@ pub struct MerchantConnectorResponse { #[schema(example = "mca_5apGeP94tMts6rg3U3kR", value_type = String)] pub id: id_type::MerchantConnectorAccountId, - /// Identifier for the business profile, if not provided default will be chosen from merchant account + /// Identifier for the profile, if not provided default will be chosen from merchant account #[schema(max_length = 64, value_type = String)] pub profile_id: id_type::ProfileId, @@ -1117,7 +1126,7 @@ pub struct MerchantConnectorResponse { #[schema(value_type = Connector, example = "stripe")] pub connector_name: String, - /// A unique label to identify the connector account created under a business profile + /// A unique label to identify the connector account created under a profile #[schema(example = "stripe_US_travel")] pub connector_label: Option, @@ -1125,7 +1134,7 @@ pub struct MerchantConnectorResponse { #[schema(example = "mca_5apGeP94tMts6rg3U3kR", value_type = String)] pub merchant_connector_id: id_type::MerchantConnectorAccountId, - /// Identifier for the business profile, if not provided default will be chosen from merchant account + /// Identifier for the profile, if not provided default will be chosen from merchant account #[schema(max_length = 64, value_type = String)] pub profile_id: id_type::ProfileId, @@ -1235,7 +1244,7 @@ pub struct MerchantConnectorListResponse { #[schema(value_type = Connector, example = "stripe")] pub connector_name: String, - /// A unique label to identify the connector account created under a business profile + /// A unique label to identify the connector account created under a profile #[schema(example = "stripe_US_travel")] pub connector_label: Option, @@ -1243,7 +1252,7 @@ pub struct MerchantConnectorListResponse { #[schema(example = "mca_5apGeP94tMts6rg3U3kR", value_type = String)] pub merchant_connector_id: id_type::MerchantConnectorAccountId, - /// Identifier for the business profile, if not provided default will be chosen from merchant account + /// Identifier for the profile, if not provided default will be chosen from merchant account #[schema(max_length = 64, value_type = String)] pub profile_id: id_type::ProfileId, @@ -1341,7 +1350,7 @@ pub struct MerchantConnectorListResponse { #[schema(value_type = Connector, example = "stripe")] pub connector_name: String, - /// A unique label to identify the connector account created under a business profile + /// A unique label to identify the connector account created under a profile #[schema(example = "stripe_US_travel")] pub connector_label: Option, @@ -1349,7 +1358,7 @@ pub struct MerchantConnectorListResponse { #[schema(example = "mca_5apGeP94tMts6rg3U3kR", value_type = String)] pub id: id_type::MerchantConnectorAccountId, - /// Identifier for the business profile, if not provided default will be chosen from merchant account + /// Identifier for the profile, if not provided default will be chosen from merchant account #[schema(max_length = 64, value_type = String)] pub profile_id: id_type::ProfileId, @@ -1806,8 +1815,8 @@ pub struct MerchantConnectorDetails { #[cfg(feature = "v1")] #[derive(Clone, Debug, Deserialize, ToSchema, Default, Serialize)] #[serde(deny_unknown_fields)] -pub struct BusinessProfileCreate { - /// The name of business profile +pub struct ProfileCreate { + /// The name of profile #[schema(max_length = 64)] pub profile_name: Option, @@ -1853,11 +1862,11 @@ pub struct BusinessProfileCreate { /// Verified Apple Pay domains for a particular profile pub applepay_verified_domains: Option>, - /// Client Secret Default expiry for all payments created under this business profile + /// Client Secret Default expiry for all payments created under this profile #[schema(example = 900)] pub session_expiry: Option, - /// Default Payment Link config for all payment links created under this business profile + /// Default Payment Link config for all payment links created under this profile pub payment_link_config: Option, /// External 3DS authentication details @@ -1924,8 +1933,8 @@ pub struct OrderFulfillmentTime(i64); #[cfg(feature = "v2")] #[derive(Clone, Debug, Deserialize, ToSchema, Default, Serialize)] #[serde(deny_unknown_fields)] -pub struct BusinessProfileCreate { - /// The name of business profile +pub struct ProfileCreate { + /// The name of profile #[schema(max_length = 64)] pub profile_name: String, @@ -1962,11 +1971,11 @@ pub struct BusinessProfileCreate { /// Verified Apple Pay domains for a particular profile pub applepay_verified_domains: Option>, - /// Client Secret Default expiry for all payments created under this business profile + /// Client Secret Default expiry for all payments created under this profile #[schema(example = 900)] pub session_expiry: Option, - /// Default Payment Link config for all payment links created under this business profile + /// Default Payment Link config for all payment links created under this profile pub payment_link_config: Option, /// External 3DS authentication details @@ -2026,16 +2035,16 @@ pub struct BusinessProfileCreate { #[cfg(feature = "v1")] #[derive(Clone, Debug, ToSchema, Serialize)] -pub struct BusinessProfileResponse { +pub struct ProfileResponse { /// The identifier for Merchant Account #[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)] pub merchant_id: id_type::MerchantId, - /// The identifier for business profile. This must be used for creating merchant accounts, payments and payouts + /// The identifier for profile. This must be used for creating merchant accounts, payments and payouts #[schema(max_length = 64, value_type = String, example = "pro_abcdefghijklmnopqrstuvwxyz")] pub profile_id: id_type::ProfileId, - /// Name of the business profile + /// Name of the profile #[schema(max_length = 64)] pub profile_name: String, @@ -2081,11 +2090,11 @@ pub struct BusinessProfileResponse { /// Verified Apple Pay domains for a particular profile pub applepay_verified_domains: Option>, - /// Client Secret Default expiry for all payments created under this business profile + /// Client Secret Default expiry for all payments created under this profile #[schema(example = 900)] pub session_expiry: Option, - /// Default Payment Link config for all payment links created under this business profile + /// Default Payment Link config for all payment links created under this profile #[schema(value_type = Option)] pub payment_link_config: Option, @@ -2148,16 +2157,16 @@ pub struct BusinessProfileResponse { #[cfg(feature = "v2")] #[derive(Clone, Debug, ToSchema, Serialize)] -pub struct BusinessProfileResponse { +pub struct ProfileResponse { /// The identifier for Merchant Account #[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)] pub merchant_id: id_type::MerchantId, - /// The identifier for business profile. This must be used for creating merchant accounts, payments and payouts + /// The identifier for profile. This must be used for creating merchant accounts, payments and payouts #[schema(max_length = 64, value_type = String, example = "pro_abcdefghijklmnopqrstuvwxyz")] pub id: id_type::ProfileId, - /// Name of the business profile + /// Name of the profile #[schema(max_length = 64)] pub profile_name: String, @@ -2186,11 +2195,11 @@ pub struct BusinessProfileResponse { /// Verified Apple Pay domains for a particular profile pub applepay_verified_domains: Option>, - /// Client Secret Default expiry for all payments created under this business profile + /// Client Secret Default expiry for all payments created under this profile #[schema(example = 900)] pub session_expiry: Option, - /// Default Payment Link config for all payment links created under this business profile + /// Default Payment Link config for all payment links created under this profile #[schema(value_type = Option)] pub payment_link_config: Option, @@ -2262,8 +2271,8 @@ pub struct BusinessProfileResponse { #[cfg(feature = "v1")] #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] #[serde(deny_unknown_fields)] -pub struct BusinessProfileUpdate { - /// The name of business profile +pub struct ProfileUpdate { + /// The name of profile #[schema(max_length = 64)] pub profile_name: Option, @@ -2309,11 +2318,11 @@ pub struct BusinessProfileUpdate { /// Verified Apple Pay domains for a particular profile pub applepay_verified_domains: Option>, - /// Client Secret Default expiry for all payments created under this business profile + /// Client Secret Default expiry for all payments created under this profile #[schema(example = 900)] pub session_expiry: Option, - /// Default Payment Link config for all payment links created under this business profile + /// Default Payment Link config for all payment links created under this profile pub payment_link_config: Option, /// External 3DS authentication details @@ -2378,8 +2387,8 @@ pub struct BusinessProfileUpdate { #[cfg(feature = "v2")] #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] #[serde(deny_unknown_fields)] -pub struct BusinessProfileUpdate { - /// The name of business profile +pub struct ProfileUpdate { + /// The name of profile #[schema(max_length = 64)] pub profile_name: Option, @@ -2416,11 +2425,11 @@ pub struct BusinessProfileUpdate { /// Verified Apple Pay domains for a particular profile pub applepay_verified_domains: Option>, - /// Client Secret Default expiry for all payments created under this business profile + /// Client Secret Default expiry for all payments created under this profile #[schema(example = 900)] pub session_expiry: Option, - /// Default Payment Link config for all payment links created under this business profile + /// Default Payment Link config for all payment links created under this profile pub payment_link_config: Option, /// External 3DS authentication details diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 41e958cb3f4..b10f8e32dba 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -75,9 +75,9 @@ impl_api_event_type!( MandateId, DisputeListConstraints, RetrieveApiKeyResponse, - BusinessProfileResponse, - BusinessProfileUpdate, - BusinessProfileCreate, + ProfileResponse, + ProfileUpdate, + ProfileCreate, RevokeApiKeyResponse, ToggleKVResponse, ToggleKVRequest, diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 0051d0a8631..4edc2ba2432 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -18,7 +18,7 @@ use crate::schema_v2::business_profile; #[cfg(feature = "v1")] #[derive(Clone, Debug, Identifiable, Queryable, Selectable, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile, primary_key(profile_id), check_for_backend(diesel::pg::Pg))] -pub struct BusinessProfile { +pub struct Profile { pub profile_id: common_utils::id_type::ProfileId, pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, @@ -60,7 +60,7 @@ pub struct BusinessProfile { #[cfg(feature = "v1")] #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile, primary_key(profile_id))] -pub struct BusinessProfileNew { +pub struct ProfileNew { pub profile_id: common_utils::id_type::ProfileId, pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, @@ -101,7 +101,7 @@ pub struct BusinessProfileNew { #[cfg(feature = "v1")] #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile)] -pub struct BusinessProfileUpdateInternal { +pub struct ProfileUpdateInternal { pub profile_name: Option, pub modified_at: time::PrimitiveDateTime, pub return_url: Option, @@ -137,8 +137,8 @@ pub struct BusinessProfileUpdateInternal { } #[cfg(feature = "v1")] -impl BusinessProfileUpdateInternal { - pub fn apply_changeset(self, source: BusinessProfile) -> BusinessProfile { +impl ProfileUpdateInternal { + pub fn apply_changeset(self, source: Profile) -> Profile { let Self { profile_name, modified_at, @@ -172,7 +172,7 @@ impl BusinessProfileUpdateInternal { dynamic_routing_algorithm, is_network_tokenization_enabled, } = self; - BusinessProfile { + Profile { profile_id: source.profile_id, merchant_id: source.merchant_id, profile_name: profile_name.unwrap_or(source.profile_name), @@ -240,7 +240,7 @@ impl BusinessProfileUpdateInternal { #[cfg(feature = "v2")] #[derive(Clone, Debug, Identifiable, Queryable, Selectable, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile, primary_key(id), check_for_backend(diesel::pg::Pg))] -pub struct BusinessProfile { +pub struct Profile { pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, pub created_at: time::PrimitiveDateTime, @@ -281,7 +281,7 @@ pub struct BusinessProfile { pub is_network_tokenization_enabled: bool, } -impl BusinessProfile { +impl Profile { #[cfg(feature = "v1")] pub fn get_id(&self) -> &common_utils::id_type::ProfileId { &self.profile_id @@ -296,7 +296,7 @@ impl BusinessProfile { #[cfg(feature = "v2")] #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile, primary_key(profile_id))] -pub struct BusinessProfileNew { +pub struct ProfileNew { pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, pub created_at: time::PrimitiveDateTime, @@ -339,7 +339,7 @@ pub struct BusinessProfileNew { #[cfg(feature = "v2")] #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = business_profile)] -pub struct BusinessProfileUpdateInternal { +pub struct ProfileUpdateInternal { pub profile_name: Option, pub modified_at: time::PrimitiveDateTime, pub return_url: Option, @@ -376,8 +376,8 @@ pub struct BusinessProfileUpdateInternal { } #[cfg(feature = "v2")] -impl BusinessProfileUpdateInternal { - pub fn apply_changeset(self, source: BusinessProfile) -> BusinessProfile { +impl ProfileUpdateInternal { + pub fn apply_changeset(self, source: Profile) -> Profile { let Self { profile_name, modified_at, @@ -412,7 +412,7 @@ impl BusinessProfileUpdateInternal { default_fallback_routing, is_network_tokenization_enabled, } = self; - BusinessProfile { + Profile { id: source.id, merchant_id: source.merchant_id, profile_name: profile_name.unwrap_or(source.profile_name), @@ -475,58 +475,6 @@ impl BusinessProfileUpdateInternal { } } -// This is being used only in the `BusinessProfileInterface` implementation for `MockDb`. -// This can be removed once the `BusinessProfileInterface` trait has been updated to use the domain -// model instead. -#[cfg(feature = "v2")] -impl From for BusinessProfile { - fn from(new: BusinessProfileNew) -> Self { - Self { - id: new.id, - merchant_id: new.merchant_id, - profile_name: new.profile_name, - created_at: new.created_at, - modified_at: new.modified_at, - return_url: new.return_url, - enable_payment_response_hash: new.enable_payment_response_hash, - payment_response_hash_key: new.payment_response_hash_key, - redirect_to_merchant_with_http_post: new.redirect_to_merchant_with_http_post, - webhook_details: new.webhook_details, - metadata: new.metadata, - is_recon_enabled: new.is_recon_enabled, - applepay_verified_domains: new.applepay_verified_domains, - payment_link_config: new.payment_link_config, - session_expiry: new.session_expiry, - authentication_connector_details: new.authentication_connector_details, - payout_link_config: new.payout_link_config, - is_connector_agnostic_mit_enabled: new.is_connector_agnostic_mit_enabled, - is_extended_card_info_enabled: new.is_extended_card_info_enabled, - extended_card_info_config: new.extended_card_info_config, - use_billing_as_payment_method_billing: new.use_billing_as_payment_method_billing, - collect_shipping_details_from_wallet_connector: new - .collect_shipping_details_from_wallet_connector, - collect_billing_details_from_wallet_connector: new - .collect_billing_details_from_wallet_connector, - outgoing_webhook_custom_http_headers: new.outgoing_webhook_custom_http_headers, - tax_connector_id: new.tax_connector_id, - is_tax_connector_enabled: new.is_tax_connector_enabled, - routing_algorithm_id: new.routing_algorithm_id, - always_collect_billing_details_from_wallet_connector: new - .always_collect_billing_details_from_wallet_connector, - always_collect_shipping_details_from_wallet_connector: new - .always_collect_shipping_details_from_wallet_connector, - order_fulfillment_time: new.order_fulfillment_time, - order_fulfillment_time_origin: new.order_fulfillment_time_origin, - frm_routing_algorithm_id: new.frm_routing_algorithm_id, - payout_routing_algorithm_id: new.payout_routing_algorithm_id, - default_fallback_routing: new.default_fallback_routing, - version: new.version, - dynamic_routing_algorithm: None, - is_network_tokenization_enabled: new.is_network_tokenization_enabled, - } - } -} - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, diesel::AsExpression)] #[diesel(sql_type = diesel::sql_types::Jsonb)] pub struct AuthenticationConnectorDetails { diff --git a/crates/diesel_models/src/query/business_profile.rs b/crates/diesel_models/src/query/business_profile.rs index da95626935d..3617155fbbe 100644 --- a/crates/diesel_models/src/query/business_profile.rs +++ b/crates/diesel_models/src/query/business_profile.rs @@ -6,21 +6,21 @@ use crate::schema::business_profile::dsl::{self, profile_id as dsl_identifier}; #[cfg(feature = "v2")] use crate::schema_v2::business_profile::dsl::{self, id as dsl_identifier}; use crate::{ - business_profile::{BusinessProfile, BusinessProfileNew, BusinessProfileUpdateInternal}, + business_profile::{Profile, ProfileNew, ProfileUpdateInternal}, errors, PgPooledConn, StorageResult, }; -impl BusinessProfileNew { - pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { +impl ProfileNew { + pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { generics::generic_insert(conn, self).await } } -impl BusinessProfile { +impl Profile { pub async fn update_by_profile_id( self, conn: &PgPooledConn, - business_profile: BusinessProfileUpdateInternal, + business_profile: ProfileUpdateInternal, ) -> StorageResult { match generics::generic_update_by_id::<::Table, _, _, _>( conn, @@ -76,7 +76,7 @@ impl BusinessProfile { .await } - pub async fn list_business_profile_by_merchant_id( + pub async fn list_profile_by_merchant_id( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, ) -> StorageResult> { diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index 159c894f17b..156ca4d2feb 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -8,7 +8,7 @@ use common_utils::{ }; use diesel_models::business_profile::{ AuthenticationConnectorDetails, BusinessPaymentLinkConfig, BusinessPayoutLinkConfig, - BusinessProfileUpdateInternal, WebhookDetails, + ProfileUpdateInternal, WebhookDetails, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -20,7 +20,7 @@ use crate::{ #[cfg(feature = "v1")] #[derive(Clone, Debug)] -pub struct BusinessProfile { +pub struct Profile { profile_id: common_utils::id_type::ProfileId, pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, @@ -59,7 +59,7 @@ pub struct BusinessProfile { } #[cfg(feature = "v1")] -pub struct BusinessProfileSetter { +pub struct ProfileSetter { pub profile_id: common_utils::id_type::ProfileId, pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, @@ -97,8 +97,8 @@ pub struct BusinessProfileSetter { } #[cfg(feature = "v1")] -impl From for BusinessProfile { - fn from(value: BusinessProfileSetter) -> Self { +impl From for Profile { + fn from(value: ProfileSetter) -> Self { Self { profile_id: value.profile_id, merchant_id: value.merchant_id, @@ -143,7 +143,7 @@ impl From for BusinessProfile { } } -impl BusinessProfile { +impl Profile { #[cfg(feature = "v1")] pub fn get_id(&self) -> &common_utils::id_type::ProfileId { &self.profile_id @@ -157,7 +157,7 @@ impl BusinessProfile { #[cfg(feature = "v1")] #[derive(Debug)] -pub struct BusinessProfileGeneralUpdate { +pub struct ProfileGeneralUpdate { pub profile_name: Option, pub return_url: Option, pub enable_payment_response_hash: Option, @@ -190,8 +190,8 @@ pub struct BusinessProfileGeneralUpdate { #[cfg(feature = "v1")] #[derive(Debug)] -pub enum BusinessProfileUpdate { - Update(Box), +pub enum ProfileUpdate { + Update(Box), RoutingAlgorithmUpdate { routing_algorithm: Option, payout_routing_algorithm: Option, @@ -211,13 +211,13 @@ pub enum BusinessProfileUpdate { } #[cfg(feature = "v1")] -impl From for BusinessProfileUpdateInternal { - fn from(business_profile_update: BusinessProfileUpdate) -> Self { +impl From for ProfileUpdateInternal { + fn from(profile_update: ProfileUpdate) -> Self { let now = date_time::now(); - match business_profile_update { - BusinessProfileUpdate::Update(update) => { - let BusinessProfileGeneralUpdate { + match profile_update { + ProfileUpdate::Update(update) => { + let ProfileGeneralUpdate { profile_name, return_url, enable_payment_response_hash, @@ -283,7 +283,7 @@ impl From for BusinessProfileUpdateInternal { is_network_tokenization_enabled, } } - BusinessProfileUpdate::RoutingAlgorithmUpdate { + ProfileUpdate::RoutingAlgorithmUpdate { routing_algorithm, payout_routing_algorithm, } => Self { @@ -319,7 +319,7 @@ impl From for BusinessProfileUpdateInternal { dynamic_routing_algorithm: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::DynamicRoutingAlgorithmUpdate { + ProfileUpdate::DynamicRoutingAlgorithmUpdate { dynamic_routing_algorithm, } => Self { profile_name: None, @@ -354,7 +354,7 @@ impl From for BusinessProfileUpdateInternal { dynamic_routing_algorithm, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::ExtendedCardInfoUpdate { + ProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, } => Self { profile_name: None, @@ -389,7 +389,7 @@ impl From for BusinessProfileUpdateInternal { dynamic_routing_algorithm: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + ProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, } => Self { profile_name: None, @@ -424,7 +424,7 @@ impl From for BusinessProfileUpdateInternal { dynamic_routing_algorithm: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::NetworkTokenizationUpdate { + ProfileUpdate::NetworkTokenizationUpdate { is_network_tokenization_enabled, } => Self { profile_name: None, @@ -465,12 +465,12 @@ impl From for BusinessProfileUpdateInternal { #[cfg(feature = "v1")] #[async_trait::async_trait] -impl super::behaviour::Conversion for BusinessProfile { - type DstType = diesel_models::business_profile::BusinessProfile; - type NewDstType = diesel_models::business_profile::BusinessProfileNew; +impl super::behaviour::Conversion for Profile { + type DstType = diesel_models::business_profile::Profile; + type NewDstType = diesel_models::business_profile::ProfileNew; async fn convert(self) -> CustomResult { - Ok(diesel_models::business_profile::BusinessProfile { + Ok(diesel_models::business_profile::Profile { profile_id: self.profile_id, merchant_id: self.merchant_id, profile_name: self.profile_name, @@ -587,7 +587,7 @@ impl super::behaviour::Conversion for BusinessProfile { } async fn construct_new(self) -> CustomResult { - Ok(diesel_models::business_profile::BusinessProfileNew { + Ok(diesel_models::business_profile::ProfileNew { profile_id: self.profile_id, merchant_id: self.merchant_id, profile_name: self.profile_name, @@ -634,7 +634,7 @@ impl super::behaviour::Conversion for BusinessProfile { #[cfg(feature = "v2")] #[derive(Clone, Debug)] -pub struct BusinessProfile { +pub struct Profile { id: common_utils::id_type::ProfileId, pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, @@ -674,7 +674,7 @@ pub struct BusinessProfile { } #[cfg(feature = "v2")] -pub struct BusinessProfileSetter { +pub struct ProfileSetter { pub id: common_utils::id_type::ProfileId, pub merchant_id: common_utils::id_type::MerchantId, pub profile_name: String, @@ -713,8 +713,8 @@ pub struct BusinessProfileSetter { } #[cfg(feature = "v2")] -impl From for BusinessProfile { - fn from(value: BusinessProfileSetter) -> Self { +impl From for Profile { + fn from(value: ProfileSetter) -> Self { Self { id: value.id, merchant_id: value.merchant_id, @@ -760,7 +760,7 @@ impl From for BusinessProfile { } } -impl BusinessProfile { +impl Profile { pub fn get_is_tax_connector_enabled(&self) -> bool { let is_tax_connector_enabled = self.is_tax_connector_enabled; match &self.tax_connector_id { @@ -782,7 +782,7 @@ impl BusinessProfile { #[cfg(feature = "v2")] #[derive(Debug)] -pub struct BusinessProfileGeneralUpdate { +pub struct ProfileGeneralUpdate { pub profile_name: Option, pub return_url: Option, pub enable_payment_response_hash: Option, @@ -810,8 +810,8 @@ pub struct BusinessProfileGeneralUpdate { #[cfg(feature = "v2")] #[derive(Debug)] -pub enum BusinessProfileUpdate { - Update(Box), +pub enum ProfileUpdate { + Update(Box), RoutingAlgorithmUpdate { routing_algorithm_id: Option, payout_routing_algorithm_id: Option, @@ -831,13 +831,13 @@ pub enum BusinessProfileUpdate { } #[cfg(feature = "v2")] -impl From for BusinessProfileUpdateInternal { - fn from(business_profile_update: BusinessProfileUpdate) -> Self { +impl From for ProfileUpdateInternal { + fn from(profile_update: ProfileUpdate) -> Self { let now = date_time::now(); - match business_profile_update { - BusinessProfileUpdate::Update(update) => { - let BusinessProfileGeneralUpdate { + match profile_update { + ProfileUpdate::Update(update) => { + let ProfileGeneralUpdate { profile_name, return_url, enable_payment_response_hash, @@ -898,7 +898,7 @@ impl From for BusinessProfileUpdateInternal { is_network_tokenization_enabled, } } - BusinessProfileUpdate::RoutingAlgorithmUpdate { + ProfileUpdate::RoutingAlgorithmUpdate { routing_algorithm_id, payout_routing_algorithm_id, } => Self { @@ -935,7 +935,7 @@ impl From for BusinessProfileUpdateInternal { is_tax_connector_enabled: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::ExtendedCardInfoUpdate { + ProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, } => Self { profile_name: None, @@ -971,7 +971,7 @@ impl From for BusinessProfileUpdateInternal { is_tax_connector_enabled: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + ProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, } => Self { profile_name: None, @@ -1007,7 +1007,7 @@ impl From for BusinessProfileUpdateInternal { is_tax_connector_enabled: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::DefaultRoutingFallbackUpdate { + ProfileUpdate::DefaultRoutingFallbackUpdate { default_fallback_routing, } => Self { profile_name: None, @@ -1043,7 +1043,7 @@ impl From for BusinessProfileUpdateInternal { is_tax_connector_enabled: None, is_network_tokenization_enabled: None, }, - BusinessProfileUpdate::NetworkTokenizationUpdate { + ProfileUpdate::NetworkTokenizationUpdate { is_network_tokenization_enabled, } => Self { profile_name: None, @@ -1085,12 +1085,12 @@ impl From for BusinessProfileUpdateInternal { #[cfg(feature = "v2")] #[async_trait::async_trait] -impl super::behaviour::Conversion for BusinessProfile { - type DstType = diesel_models::business_profile::BusinessProfile; - type NewDstType = diesel_models::business_profile::BusinessProfileNew; +impl super::behaviour::Conversion for Profile { + type DstType = diesel_models::business_profile::Profile; + type NewDstType = diesel_models::business_profile::ProfileNew; async fn convert(self) -> CustomResult { - Ok(diesel_models::business_profile::BusinessProfile { + Ok(diesel_models::business_profile::Profile { id: self.id, merchant_id: self.merchant_id, profile_name: self.profile_name, @@ -1210,7 +1210,7 @@ impl super::behaviour::Conversion for BusinessProfile { } async fn construct_new(self) -> CustomResult { - Ok(diesel_models::business_profile::BusinessProfileNew { + Ok(diesel_models::business_profile::ProfileNew { id: self.id, merchant_id: self.merchant_id, profile_name: self.profile_name, diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index a289881e3a3..75bd7740026 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -100,7 +100,7 @@ pub enum ApiErrorResponse { #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Merchant connector account does not exist in our records")] MerchantConnectorAccountNotFound { id: String }, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Business profile with the given id '{id}' does not exist in our records")] - BusinessProfileNotFound { id: String }, + ProfileNotFound { id: String }, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Poll with the given id '{id}' does not exist in our records")] PollNotFound { id: String }, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Resource ID does not exist in our records")] @@ -408,7 +408,7 @@ impl ErrorSwitch for ApiErrorRespon Self::MerchantConnectorAccountNotFound {id } => { AER::NotFound(ApiError::new("HE", 2, "Merchant connector account does not exist in our records", Some(Extra {reason: Some(format!("{id} does not exist")), ..Default::default()}))) } - Self::BusinessProfileNotFound { id } => { + Self::ProfileNotFound { id } => { AER::NotFound(ApiError::new("HE", 2, format!("Business profile with the given id {id} does not exist"), None)) } Self::PollNotFound { .. } => { diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index d1746a63dde..0c9010421ba 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -53,7 +53,7 @@ Never share your secret api keys. Keep them guarded and secure. ), tags( (name = "Merchant Account", description = "Create and manage merchant accounts"), - (name = "Business Profile", description = "Create and manage business profiles"), + (name = "Profile", description = "Create and manage profiles"), (name = "Merchant Connector Account", description = "Create and manage merchant connector accounts"), (name = "Payments", description = "Create and manage one-time payments, recurring payments and mandates"), (name = "Refunds", description = "Create and manage refunds for successful payments"), @@ -136,12 +136,12 @@ Never share your secret api keys. Keep them guarded and secure. routes::payment_method::payment_method_update_api, routes::payment_method::payment_method_delete_api, - // Routes for Business Profile - routes::business_profile::business_profile_create, - routes::business_profile::business_profile_list, - routes::business_profile::business_profile_retrieve, - routes::business_profile::business_profile_update, - routes::business_profile::business_profile_delete, + // Routes for Profile + routes::profile::profile_create, + routes::profile::profile_list, + routes::profile::profile_retrieve, + routes::profile::profile_update, + routes::profile::profile_delete, // Routes for disputes routes::disputes::retrieve_dispute, @@ -283,8 +283,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::MerchantConnectorDetailsWrap, api_models::admin::MerchantConnectorDetails, api_models::admin::MerchantConnectorWebhookDetails, - api_models::admin::BusinessProfileCreate, - api_models::admin::BusinessProfileResponse, + api_models::admin::ProfileCreate, + api_models::admin::ProfileResponse, api_models::admin::BusinessPaymentLinkConfig, api_models::admin::PaymentLinkConfigRequest, api_models::admin::PaymentLinkConfig, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index b7771dc5e0a..ca2974d011a 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -53,7 +53,7 @@ Never share your secret api keys. Keep them guarded and secure. ), tags( (name = "Merchant Account", description = "Create and manage merchant accounts"), - (name = "Business Profile", description = "Create and manage business profiles"), + (name = "Profile", description = "Create and manage profiles"), (name = "Merchant Connector Account", description = "Create and manage merchant connector accounts"), (name = "Payments", description = "Create and manage one-time payments, recurring payments and mandates"), (name = "Refunds", description = "Create and manage refunds for successful payments"), @@ -85,20 +85,20 @@ Never share your secret api keys. Keep them guarded and secure. routes::merchant_account::merchant_account_create, routes::merchant_account::merchant_account_retrieve, routes::merchant_account::merchant_account_update, - routes::merchant_account::business_profiles_list, + routes::merchant_account::profiles_list, - // Routes for business profile - routes::business_profile::business_profile_create, - routes::business_profile::business_profile_retrieve, - routes::business_profile::business_profile_update, - routes::business_profile::connector_list, + // Routes for profile + routes::profile::profile_create, + routes::profile::profile_retrieve, + routes::profile::profile_update, + routes::profile::connector_list, - // Routes for routing under business profile - routes::business_profile::routing_link_config, - routes::business_profile::routing_unlink_config, - routes::business_profile::routing_update_default_config, - routes::business_profile::routing_retrieve_default_config, - routes::business_profile::routing_retrieve_linked_config, + // Routes for routing under profile + routes::profile::routing_link_config, + routes::profile::routing_unlink_config, + routes::profile::routing_update_default_config, + routes::profile::routing_retrieve_default_config, + routes::profile::routing_retrieve_linked_config, // Routes for routing routes::routing::routing_create_config, @@ -209,8 +209,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::MerchantConnectorDetailsWrap, api_models::admin::MerchantConnectorDetails, api_models::admin::MerchantConnectorWebhookDetails, - api_models::admin::BusinessProfileCreate, - api_models::admin::BusinessProfileResponse, + api_models::admin::ProfileCreate, + api_models::admin::ProfileResponse, api_models::admin::BusinessPaymentLinkConfig, api_models::admin::PaymentLinkConfigRequest, api_models::admin::PaymentLinkConfig, diff --git a/crates/openapi/src/routes.rs b/crates/openapi/src/routes.rs index 406c88e2e3f..453f7f557d3 100644 --- a/crates/openapi/src/routes.rs +++ b/crates/openapi/src/routes.rs @@ -2,7 +2,6 @@ pub mod api_keys; pub mod blocklist; -pub mod business_profile; pub mod customers; pub mod disputes; pub mod gsm; @@ -15,6 +14,7 @@ pub mod payment_method; pub mod payments; pub mod payouts; pub mod poll; +pub mod profile; pub mod refunds; pub mod routing; pub mod webhook_events; diff --git a/crates/openapi/src/routes/disputes.rs b/crates/openapi/src/routes/disputes.rs index c5875d67a9a..977f0b775f8 100644 --- a/crates/openapi/src/routes/disputes.rs +++ b/crates/openapi/src/routes/disputes.rs @@ -43,7 +43,7 @@ pub async fn retrieve_dispute() {} )] pub async fn retrieve_disputes_list() {} -/// Disputes - List Disputes for The Given Business Profiles +/// Disputes - List Disputes for The Given Profiles /// Lists all the Disputes for a merchant #[utoipa::path( get, @@ -65,7 +65,7 @@ pub async fn retrieve_disputes_list() {} (status = 401, description = "Unauthorized request") ), tag = "Disputes", - operation_id = "List Disputes for The given Business Profiles", + operation_id = "List Disputes for The given Profiles", security(("api_key" = [])) )] pub async fn retrieve_disputes_list_profile() {} diff --git a/crates/openapi/src/routes/merchant_account.rs b/crates/openapi/src/routes/merchant_account.rs index 16533c6a61c..b92285245a4 100644 --- a/crates/openapi/src/routes/merchant_account.rs +++ b/crates/openapi/src/routes/merchant_account.rs @@ -270,20 +270,40 @@ pub async fn delete_merchant_account() {} )] pub async fn merchant_account_kv_status() {} +/// Merchant Connector - List +/// +/// List Merchant Connector Details for the merchant +#[utoipa::path( + get, + path = "/accounts/{account_id}/profile/connectors", + params( + ("account_id" = String, Path, description = "The unique identifier for the merchant account"), + ), + responses( + (status = 200, description = "Merchant Connector list retrieved successfully", body = Vec), + (status = 404, description = "Merchant Connector does not exist in records"), + (status = 401, description = "Unauthorized request") + ), + tag = "Merchant Connector Account", + operation_id = "List all Merchant Connectors for The given Profile", + security(("admin_api_key" = [])) +)] +pub async fn payment_connector_list_profile() {} + #[cfg(feature = "v2")] -/// Business Profile - List +/// Profile - List /// -/// List business profiles for an Merchant +/// List profiles for an Merchant #[utoipa::path( get, path = "/v2/merchant_accounts/{account_id}/profiles", params (("account_id" = String, Path, description = "The unique identifier for the Merchant")), responses( - (status = 200, description = "Business profile list retrieved successfully", body = Vec), + (status = 200, description = "profile list retrieved successfully", body = Vec), (status = 400, description = "Invalid data") ), tag = "Merchant Account", - operation_id = "List Business Profiles", + operation_id = "List Profiles", security(("admin_api_key" = [])) )] -pub async fn business_profiles_list() {} +pub async fn profiles_list() {} diff --git a/crates/openapi/src/routes/merchant_connector_account.rs b/crates/openapi/src/routes/merchant_connector_account.rs index a7c91ce128c..a9cabde8af4 100644 --- a/crates/openapi/src/routes/merchant_connector_account.rs +++ b/crates/openapi/src/routes/merchant_connector_account.rs @@ -22,7 +22,7 @@ ) ), ( - "Create a merchant connector account under a specific business profile" = ( + "Create a merchant connector account under a specific profile" = ( value = json!({ "connector_type": "payment_processor", "connector_name": "adyen", @@ -85,7 +85,7 @@ pub async fn connector_create() {} ) ), ( - "Create a merchant connector account under a specific business profile" = ( + "Create a merchant connector account under a specific profile" = ( value = json!({ "connector_type": "payment_processor", "connector_name": "adyen", diff --git a/crates/openapi/src/routes/payments.rs b/crates/openapi/src/routes/payments.rs index 1ed8c541bfd..a3fe0cd941c 100644 --- a/crates/openapi/src/routes/payments.rs +++ b/crates/openapi/src/routes/payments.rs @@ -459,7 +459,7 @@ pub fn payments_cancel() {} )] pub fn payments_list() {} -/// Business Profile level Payments - List +/// Profile level Payments - List /// /// To list the payments #[utoipa::path( diff --git a/crates/openapi/src/routes/business_profile.rs b/crates/openapi/src/routes/profile.rs similarity index 65% rename from crates/openapi/src/routes/business_profile.rs rename to crates/openapi/src/routes/profile.rs index 89003da08ee..f726c76db84 100644 --- a/crates/openapi/src/routes/business_profile.rs +++ b/crates/openapi/src/routes/profile.rs @@ -1,7 +1,9 @@ +// ******************************************** V1 profile routes ******************************************** // + #[cfg(feature = "v1")] -/// Business Profile - Create +/// Profile - Create /// -/// Creates a new *business profile* for a merchant +/// Creates a new *profile* for a merchant #[utoipa::path( post, path = "/account/{account_id}/business_profile", @@ -9,15 +11,15 @@ ("account_id" = String, Path, description = "The unique identifier for the merchant account") ), request_body( - content = BusinessProfileCreate, + content = ProfileCreate, examples( ( - "Create a business profile with minimal fields" = ( + "Create a profile with minimal fields" = ( value = json!({}) ) ), ( - "Create a business profile with profile name" = ( + "Create a profile with profile name" = ( value = json!({ "profile_name": "shoe_business" }) @@ -26,78 +28,31 @@ ) ), responses( - (status = 200, description = "Business Account Created", body = BusinessProfileResponse), + (status = 200, description = "Profile Created", body = ProfileResponse), (status = 400, description = "Invalid data") ), - tag = "Business Profile", - operation_id = "Create A Business Profile", + tag = "Profile", + operation_id = "Create A Profile", security(("admin_api_key" = [])) )] -pub async fn business_profile_create() {} - -#[cfg(feature = "v2")] -/// Business Profile - Create -/// -/// Creates a new *business profile* for a merchant -#[utoipa::path( - post, - path = "/v2/profiles", - request_body( - content = BusinessProfileCreate, - examples( - ( - "Create a business profile with profile name" = ( - value = json!({ - "profile_name": "shoe_business" - }) - ) - ) - ) - ), - responses( - (status = 200, description = "Business Account Created", body = BusinessProfileResponse), - (status = 400, description = "Invalid data") - ), - tag = "Business Profile", - operation_id = "Create A Business Profile", - security(("admin_api_key" = [])) -)] -pub async fn business_profile_create() {} - -/// Business Profile - List -/// -/// Lists all the *business profiles* under a merchant -#[utoipa::path( - get, - path = "/account/{account_id}/business_profile", - params ( - ("account_id" = String, Path, description = "Merchant Identifier"), - ), - responses( - (status = 200, description = "Business profiles Retrieved", body = Vec) - ), - tag = "Business Profile", - operation_id = "List Business Profiles", - security(("api_key" = [])) -)] -pub async fn business_profile_list() {} +pub async fn profile_create() {} #[cfg(feature = "v1")] -/// Business Profile - Update +/// Profile - Update /// -/// Update the *business profile* +/// Update the *profile* #[utoipa::path( post, path = "/account/{account_id}/business_profile/{profile_id}", params( ("account_id" = String, Path, description = "The unique identifier for the merchant account"), - ("profile_id" = String, Path, description = "The unique identifier for the business profile") + ("profile_id" = String, Path, description = "The unique identifier for the profile") ), request_body( - content = BusinessProfileCreate, + content = ProfileCreate, examples( ( - "Update business profile with profile name fields" = ( + "Update profile with profile name fields" = ( value = json!({ "profile_name" : "shoe_business" }) @@ -105,133 +60,142 @@ pub async fn business_profile_list() {} ) )), responses( - (status = 200, description = "Business Profile Updated", body = BusinessProfileResponse), + (status = 200, description = "Profile Updated", body = ProfileResponse), (status = 400, description = "Invalid data") ), - tag = "Business Profile", - operation_id = "Update a Business Profile", + tag = "Profile", + operation_id = "Update a Profile", security(("admin_api_key" = [])) )] -pub async fn business_profile_update() {} +pub async fn profile_update() {} -#[cfg(feature = "v2")] -/// Business Profile - Update +#[cfg(feature = "v1")] +/// Profile - Retrieve /// -/// Update the *business profile* +/// Retrieve existing *profile* #[utoipa::path( - put, - path = "/v2/profiles/{profile_id}", + get, + path = "/account/{account_id}/business_profile/{profile_id}", params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile") + ("account_id" = String, Path, description = "The unique identifier for the merchant account"), + ("profile_id" = String, Path, description = "The unique identifier for the profile") ), - request_body( - content = BusinessProfileCreate, - examples( - ( - "Update business profile with profile name fields" = ( - value = json!({ - "profile_name" : "shoe_business" - }) - ) - ) - )), responses( - (status = 200, description = "Business Profile Updated", body = BusinessProfileResponse), + (status = 200, description = "Profile Updated", body = ProfileResponse), (status = 400, description = "Invalid data") ), - tag = "Business Profile", - operation_id = "Update a Business Profile", + tag = "Profile", + operation_id = "Retrieve a Profile", security(("admin_api_key" = [])) )] -pub async fn business_profile_update() {} +pub async fn profile_retrieve() {} + +// ******************************************** Common profile routes ******************************************** // -/// Business Profile - Delete +/// Profile - Delete /// -/// Delete the *business profile* +/// Delete the *profile* #[utoipa::path( delete, path = "/account/{account_id}/business_profile/{profile_id}", params( ("account_id" = String, Path, description = "The unique identifier for the merchant account"), - ("profile_id" = String, Path, description = "The unique identifier for the business profile") + ("profile_id" = String, Path, description = "The unique identifier for the profile") ), responses( - (status = 200, description = "Business profiles Deleted", body = bool), + (status = 200, description = "Profiles Deleted", body = bool), (status = 400, description = "Invalid data") ), - tag = "Business Profile", - operation_id = "Delete the Business Profile", + tag = "Profile", + operation_id = "Delete the Profile", security(("api_key" = [])) )] -pub async fn business_profile_delete() {} +pub async fn profile_delete() {} -#[cfg(feature = "v1")] -/// Business Profile - Retrieve +/// Profile - List /// -/// Retrieve existing *business profile* +/// Lists all the *profiles* under a merchant #[utoipa::path( get, - path = "/account/{account_id}/business_profile/{profile_id}", - params( - ("account_id" = String, Path, description = "The unique identifier for the merchant account"), - ("profile_id" = String, Path, description = "The unique identifier for the business profile") + path = "/account/{account_id}/business_profile", + params ( + ("account_id" = String, Path, description = "Merchant Identifier"), ), responses( - (status = 200, description = "Business Profile Updated", body = BusinessProfileResponse), - (status = 400, description = "Invalid data") + (status = 200, description = "Profiles Retrieved", body = Vec) ), - tag = "Business Profile", - operation_id = "Retrieve a Business Profile", - security(("admin_api_key" = [])) + tag = "Profile", + operation_id = "List Profiles", + security(("api_key" = [])) )] -pub async fn business_profile_retrieve() {} +pub async fn profile_list() {} + +// ******************************************** V2 profile routes ******************************************** // #[cfg(feature = "v2")] -/// Business Profile - Retrieve +/// Profile - Create /// -/// Retrieve existing *business profile* +/// Creates a new *profile* for a merchant #[utoipa::path( - get, - path = "/v2/profiles/{profile_id}", - params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile") + post, + path = "/v2/profiles", + request_body( + content = ProfileCreate, + examples( + ( + "Create a profile with profile name" = ( + value = json!({ + "profile_name": "shoe_business" + }) + ) + ) + ) ), responses( - (status = 200, description = "Business Profile Updated", body = BusinessProfileResponse), + (status = 200, description = "Account Created", body = ProfileResponse), (status = 400, description = "Invalid data") ), - tag = "Business Profile", - operation_id = "Retrieve a Business Profile", + tag = "Profile", + operation_id = "Create A Profile", security(("admin_api_key" = [])) )] -pub async fn business_profile_retrieve() {} +pub async fn profile_create() {} #[cfg(feature = "v2")] -/// Business Profile - Retrieve Active Routing Algorithm +/// Profile - Update /// -/// Retrieve active routing algorithm under the business profile +/// Update the *profile* #[utoipa::path( - get, - path = "/v2/profiles/{profile_id}/routing_algorithm", + put, + path = "/v2/profiles/{profile_id}", params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile"), - ("limit" = Option, Query, description = "The number of records of the algorithms to be returned"), - ("offset" = Option, Query, description = "The record offset of the algorithm from which to start gathering the results")), + ("profile_id" = String, Path, description = "The unique identifier for the profile") + ), + request_body( + content = ProfileCreate, + examples( + ( + "Update profile with profile name fields" = ( + value = json!({ + "profile_name" : "shoe_business" + }) + ) + ) + )), responses( - (status = 200, description = "Successfully retrieved active config", body = LinkedRoutingConfigRetrieveResponse), - (status = 500, description = "Internal server error"), - (status = 404, description = "Resource missing"), - (status = 403, description = "Forbidden") + (status = 200, description = "Profile Updated", body = ProfileResponse), + (status = 400, description = "Invalid data") ), - tag = "Business Profile", - operation_id = "Retrieve the active routing algorithm under the business profile", - security(("api_key" = []), ("jwt_key" = [])) + tag = "Profile", + operation_id = "Update a Profile", + security(("admin_api_key" = [])) )] -pub async fn routing_retrieve_linked_config() {} +pub async fn profile_update() {} + #[cfg(feature = "v2")] -/// Business Profile - Activate routing algorithm +/// Profile - Activate routing algorithm /// -/// Activates a routing algorithm under a business profile +/// Activates a routing algorithm under a profile #[utoipa::path( patch, path = "/v2/profiles/{profile_id}/activate_routing_algorithm", @@ -244,7 +208,7 @@ pub async fn routing_retrieve_linked_config() {} ) ))), params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile"), + ("profile_id" = String, Path, description = "The unique identifier for the profile"), ), responses( (status = 200, description = "Routing Algorithm is activated", body = RoutingDictionaryRecord), @@ -252,21 +216,21 @@ pub async fn routing_retrieve_linked_config() {} (status = 404, description = "Resource missing"), (status = 400, description = "Bad request") ), - tag = "Business Profile", - operation_id = "Activates a routing algorithm under a business profile", + tag = "Profile", + operation_id = "Activates a routing algorithm under a profile", security(("api_key" = []), ("jwt_key" = [])) )] pub async fn routing_link_config() {} #[cfg(feature = "v2")] -/// Business Profile - Deactivate routing algorithm +/// Profile - Deactivate routing algorithm /// -/// Deactivates a routing algorithm under a business profile +/// Deactivates a routing algorithm under a profile #[utoipa::path( patch, path = "/v2/profiles/{profile_id}/deactivate_routing_algorithm", params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile"), + ("profile_id" = String, Path, description = "The unique identifier for the profile"), ), responses( (status = 200, description = "Successfully deactivated routing config", body = RoutingDictionaryRecord), @@ -275,22 +239,22 @@ pub async fn routing_link_config() {} (status = 403, description = "Malformed request"), (status = 422, description = "Unprocessable request") ), - tag = "Business Profile", - operation_id = " Deactivates a routing algorithm under a business profile", + tag = "Profile", + operation_id = " Deactivates a routing algorithm under a profile", security(("api_key" = []), ("jwt_key" = [])) )] pub async fn routing_unlink_config() {} #[cfg(feature = "v2")] -/// Business Profile - Update Default Fallback Routing Algorithm +/// Profile - Update Default Fallback Routing Algorithm /// -/// Update the default fallback routing algorithm for the business profile +/// Update the default fallback routing algorithm for the profile #[utoipa::path( - post, + patch, path = "/v2/profiles/{profile_id}/fallback_routing", request_body = Vec, params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile"), + ("profile_id" = String, Path, description = "The unique identifier for the profile"), ), responses( (status = 200, description = "Successfully updated the default fallback routing algorithm", body = Vec), @@ -298,28 +262,71 @@ pub async fn routing_unlink_config() {} (status = 400, description = "Malformed request"), (status = 422, description = "Unprocessable request") ), - tag = "Business Profile", - operation_id = "Update the default fallback routing algorithm for the business profile", + tag = "Profile", + operation_id = "Update the default fallback routing algorithm for the profile", security(("api_key" = []), ("jwt_key" = [])) )] pub async fn routing_update_default_config() {} #[cfg(feature = "v2")] -/// Business Profile - Retrieve Default Fallback Routing Algorithm +/// Profile - Retrieve +/// +/// Retrieve existing *profile* +#[utoipa::path( + get, + path = "/v2/profiles/{profile_id}", + params( + ("profile_id" = String, Path, description = "The unique identifier for the profile") + ), + responses( + (status = 200, description = "Profile Updated", body = ProfileResponse), + (status = 400, description = "Invalid data") + ), + tag = "Profile", + operation_id = "Retrieve a Profile", + security(("admin_api_key" = [])) +)] +pub async fn profile_retrieve() {} + +#[cfg(feature = "v2")] +/// Profile - Retrieve Active Routing Algorithm +/// +/// Retrieve active routing algorithm under the profile +#[utoipa::path( + get, + path = "/v2/profiles/{profile_id}/routing_algorithm", + params( + ("profile_id" = String, Path, description = "The unique identifier for the profile"), + ("limit" = Option, Query, description = "The number of records of the algorithms to be returned"), + ("offset" = Option, Query, description = "The record offset of the algorithm from which to start gathering the results")), + responses( + (status = 200, description = "Successfully retrieved active config", body = LinkedRoutingConfigRetrieveResponse), + (status = 500, description = "Internal server error"), + (status = 404, description = "Resource missing"), + (status = 403, description = "Forbidden") + ), + tag = "Profile", + operation_id = "Retrieve the active routing algorithm under the profile", + security(("api_key" = []), ("jwt_key" = [])) +)] +pub async fn routing_retrieve_linked_config() {} + +#[cfg(feature = "v2")] +/// Profile - Retrieve Default Fallback Routing Algorithm /// -/// Retrieve the default fallback routing algorithm for the business profile +/// Retrieve the default fallback routing algorithm for the profile #[utoipa::path( get, path = "/v2/profiles/{profile_id}/fallback_routing", params( - ("profile_id" = String, Path, description = "The unique identifier for the business profile"), + ("profile_id" = String, Path, description = "The unique identifier for the profile"), ), responses( (status = 200, description = "Successfully retrieved default fallback routing algorithm", body = Vec), (status = 500, description = "Internal server error") ), - tag = "Business Profile", - operation_id = "Retrieve the default fallback routing algorithm for the business profile", + tag = "Profile", + operation_id = "Retrieve the default fallback routing algorithm for the profile", security(("api_key" = []), ("jwt_key" = [])) )] pub async fn routing_retrieve_default_config() {} diff --git a/crates/openapi/src/routes/webhook_events.rs b/crates/openapi/src/routes/webhook_events.rs index c754f59e517..d6bfa55f398 100644 --- a/crates/openapi/src/routes/webhook_events.rs +++ b/crates/openapi/src/routes/webhook_events.rs @@ -1,6 +1,6 @@ /// Events - List /// -/// List all Events associated with a Merchant Account or Business Profile. +/// List all Events associated with a Merchant Account or Profile. #[utoipa::path( get, path = "/events/{merchant_id}", @@ -43,14 +43,14 @@ ( "profile_id" = Option, Query, - description = "Only include Events associated with the Business Profile identified by the specified Business Profile ID." + description = "Only include Events associated with the Profile identified by the specified Profile ID." ), ), responses( (status = 200, description = "List of Events retrieved successfully", body = Vec), ), tag = "Event", - operation_id = "List all Events associated with a Merchant Account or Business Profile", + operation_id = "List all Events associated with a Merchant Account or Profile", security(("admin_api_key" = [])) )] pub fn list_initial_webhook_delivery_attempts() {} diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index 547b6e1e54a..efe0ac157e0 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -611,7 +611,7 @@ impl From for StripeErrorCode { object: "authentication".to_owned(), id, }, - errors::ApiErrorResponse::BusinessProfileNotFound { id } => Self::ResourceMissing { + errors::ApiErrorResponse::ProfileNotFound { id } => Self::ResourceMissing { object: "business_profile".to_owned(), id, }, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 95b02f0cdb3..4000add34eb 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -404,8 +404,8 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { let mut domain_merchant_account = domain::MerchantAccount::from(merchant_account); - CreateBusinessProfile::new(self.primary_business_details.clone()) - .create_business_profiles(state, &mut domain_merchant_account, &key_store) + CreateProfile::new(self.primary_business_details.clone()) + .create_profiles(state, &mut domain_merchant_account, &key_store) .await?; Ok(domain_merchant_account) @@ -470,21 +470,21 @@ impl CreateOrValidateOrganization { } #[cfg(all(feature = "v1", feature = "olap"))] -enum CreateBusinessProfile { - /// Create business profiles from primary business details - /// If there is only one business profile created, then set this profile as default +enum CreateProfile { + /// Create profiles from primary business details + /// If there is only one profile created, then set this profile as default CreateFromPrimaryBusinessDetails { primary_business_details: Vec, }, - /// Create a default business profile, set this as default profile - CreateDefaultBusinessProfile, + /// Create a default profile, set this as default profile + CreateDefaultProfile, } #[cfg(all(feature = "v1", feature = "olap"))] -impl CreateBusinessProfile { - /// Create a new business profile action from the given information - /// If primary business details exist, then create business profiles from them - /// If primary business details are empty, then create default business profile +impl CreateProfile { + /// Create a new profile action from the given information + /// If primary business details exist, then create profiles from them + /// If primary business details are empty, then create default profile fn new(primary_business_details: Option>) -> Self { match primary_business_details { Some(primary_business_details) if !primary_business_details.is_empty() => { @@ -492,11 +492,11 @@ impl CreateBusinessProfile { primary_business_details, } } - _ => Self::CreateDefaultBusinessProfile, + _ => Self::CreateDefaultProfile, } } - async fn create_business_profiles( + async fn create_profiles( &self, state: &SessionState, merchant_account: &mut domain::MerchantAccount, @@ -506,7 +506,7 @@ impl CreateBusinessProfile { Self::CreateFromPrimaryBusinessDetails { primary_business_details, } => { - let business_profiles = Self::create_business_profiles_for_each_business_details( + let business_profiles = Self::create_profiles_for_each_business_details( state, merchant_account.clone(), primary_business_details, @@ -521,7 +521,7 @@ impl CreateBusinessProfile { .map(|business_profile| business_profile.get_id().to_owned()) } } - Self::CreateDefaultBusinessProfile => { + Self::CreateDefaultProfile => { let business_profile = self .create_default_business_profile(state, merchant_account.clone(), key_store) .await?; @@ -533,16 +533,16 @@ impl CreateBusinessProfile { Ok(()) } - /// Create default business profile + /// Create default profile async fn create_default_business_profile( &self, state: &SessionState, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - ) -> RouterResult { + ) -> RouterResult { let business_profile = create_and_insert_business_profile( state, - api_models::admin::BusinessProfileCreate::default(), + api_models::admin::ProfileCreate::default(), merchant_account.clone(), key_store, ) @@ -551,40 +551,38 @@ impl CreateBusinessProfile { Ok(business_profile) } - /// Create business profile for each primary_business_details, + /// Create profile for each primary_business_details, /// If there is no default profile in merchant account and only one primary_business_detail - /// is available, then create a default business profile. - async fn create_business_profiles_for_each_business_details( + /// is available, then create a default profile. + async fn create_profiles_for_each_business_details( state: &SessionState, merchant_account: domain::MerchantAccount, primary_business_details: &Vec, key_store: &domain::MerchantKeyStore, - ) -> RouterResult> { + ) -> RouterResult> { let mut business_profiles_vector = Vec::with_capacity(primary_business_details.len()); // This must ideally be run in a transaction, - // if there is an error in inserting some business profile, because of unique constraints + // if there is an error in inserting some profile, because of unique constraints // the whole query must be rolled back for business_profile in primary_business_details { let profile_name = format!("{}_{}", business_profile.country, business_profile.business); - let business_profile_create_request = api_models::admin::BusinessProfileCreate { + let profile_create_request = api_models::admin::ProfileCreate { profile_name: Some(profile_name), ..Default::default() }; create_and_insert_business_profile( state, - business_profile_create_request, + profile_create_request, merchant_account.clone(), key_store, ) .await - .map_err(|business_profile_insert_error| { - crate::logger::warn!( - "Business profile already exists {business_profile_insert_error:?}" - ); + .map_err(|profile_insert_error| { + crate::logger::warn!("Profile already exists {profile_insert_error:?}"); }) .map(|business_profile| business_profiles_vector.push(business_profile)) .ok(); @@ -606,6 +604,12 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { let publishable_key = create_merchant_publishable_key(); let db = &*state.store; + let metadata = self.get_metadata_as_secret().change_context( + errors::ApiErrorResponse::InvalidDataValue { + field_name: "metadata", + }, + )?; + let merchant_details = self.get_merchant_details_as_secret().change_context( errors::ApiErrorResponse::InvalidDataValue { field_name: "merchant_details", @@ -653,7 +657,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { }) .await?, publishable_key, - metadata: self.metadata, + metadata, storage_scheme: MerchantStorageScheme::PostgresOnly, created_at: date_time::now(), modified_at: date_time::now(), @@ -751,8 +755,8 @@ pub async fn get_merchant_account( #[cfg(feature = "v1")] /// For backwards compatibility, whenever new business labels are passed in -/// primary_business_details, create a business profile -pub async fn create_business_profile_from_business_labels( +/// primary_business_details, create a profile +pub async fn create_profile_from_business_labels( state: &SessionState, db: &dyn StorageInterface, key_store: &domain::MerchantKeyStore, @@ -783,27 +787,25 @@ pub async fn create_business_profile_from_business_labels( for business_profile in business_profiles_to_create { let profile_name = format!("{}_{}", business_profile.country, business_profile.business); - let business_profile_create_request = admin_types::BusinessProfileCreate { + let profile_create_request = admin_types::ProfileCreate { profile_name: Some(profile_name), ..Default::default() }; - let business_profile_create_result = create_and_insert_business_profile( + let profile_create_result = create_and_insert_business_profile( state, - business_profile_create_request, + profile_create_request, merchant_account.clone(), key_store, ) .await - .map_err(|business_profile_insert_error| { + .map_err(|profile_insert_error| { // If there is any duplicate error, we need not take any action - crate::logger::warn!( - "Business profile already exists {business_profile_insert_error:?}" - ); + crate::logger::warn!("Profile already exists {profile_insert_error:?}"); }); - // If a business_profile is created, then unset the default profile - if business_profile_create_result.is_ok() && merchant_account.default_profile.is_some() { + // If a profile is created, then unset the default profile + if profile_create_result.is_ok() && merchant_account.default_profile.is_some() { let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile; db.update_merchant( key_manager_state, @@ -896,11 +898,11 @@ impl MerchantAccountUpdateBridge for api::MerchantAccountUpdate { #[cfg(any(feature = "v1", feature = "v2"))] // In order to support backwards compatibility, if a business_labels are passed in the update - // call, then create new business_profiles with the profile_name as business_label + // call, then create new profiles with the profile_name as business_label self.primary_business_details .clone() .async_map(|primary_business_details| async { - let _ = create_business_profile_from_business_labels( + let _ = create_profile_from_business_labels( state, db, key_store, @@ -1912,7 +1914,7 @@ struct DefaultFallbackRoutingConfigUpdate<'a> { routable_connector: &'a Option, merchant_connector_id: &'a id_type::MerchantConnectorAccountId, store: &'a dyn StorageInterface, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, key_manager_state: &'a KeyManagerState, } @@ -1921,7 +1923,7 @@ impl<'a> DefaultFallbackRoutingConfigUpdate<'a> { async fn retrieve_and_update_default_fallback_routing_algorithm_if_routable_connector_exists( &self, ) -> RouterResult<()> { - let profile_wrapper = BusinessProfileWrapper::new(self.business_profile.clone()); + let profile_wrapper = ProfileWrapper::new(self.business_profile.clone()); let default_routing_config_for_profile = &mut profile_wrapper.get_default_fallback_list_of_connector_under_profile()?; if let Some(routable_connector_val) = self.routable_connector { @@ -2317,7 +2319,7 @@ trait MerchantConnectorAccountCreateBridge { self, state: &SessionState, key_store: domain::MerchantKeyStore, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_manager_state: &KeyManagerState, ) -> RouterResult; @@ -2327,7 +2329,7 @@ trait MerchantConnectorAccountCreateBridge { db: &dyn StorageInterface, key_manager_state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - ) -> RouterResult; + ) -> RouterResult; } #[cfg(all(feature = "v2", feature = "olap",))] @@ -2337,7 +2339,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { self, state: &SessionState, key_store: domain::MerchantKeyStore, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_manager_state: &KeyManagerState, ) -> RouterResult { // If connector label is not passed in the request, generate one @@ -2462,9 +2464,9 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { db: &dyn StorageInterface, key_manager_state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - ) -> RouterResult { + ) -> RouterResult { let profile_id = self.profile_id; - // Check whether this business profile belongs to the merchant + // Check whether this profile belongs to the merchant let business_profile = core_utils::validate_and_get_business_profile( db, @@ -2474,8 +2476,8 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + .get_required_value("Profile") + .change_context(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -2490,7 +2492,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { self, state: &SessionState, key_store: domain::MerchantKeyStore, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_manager_state: &KeyManagerState, ) -> RouterResult { // If connector label is not passed in the request, generate one @@ -2634,7 +2636,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { db: &dyn StorageInterface, key_manager_state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - ) -> RouterResult { + ) -> RouterResult { match self.profile_id.or(merchant_account.default_profile.clone()) { Some(profile_id) => { // Check whether this business profile belongs to the merchant @@ -2647,12 +2649,10 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") - .change_context( - errors::ApiErrorResponse::BusinessProfileNotFound { - id: profile_id.get_string_repr().to_owned(), - }, - )?; + .get_required_value("Profile") + .change_context(errors::ApiErrorResponse::ProfileNotFound { + id: profile_id.get_string_repr().to_owned(), + })?; Ok(business_profile) } @@ -2667,9 +2667,9 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { merchant_account.get_id(), ) .await - .to_not_found_response( - errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_name }, - )?; + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { + id: profile_name, + })?; Ok(business_profile) } @@ -3354,17 +3354,13 @@ pub fn get_frm_config_as_secret( #[cfg(feature = "v1")] pub async fn create_and_insert_business_profile( state: &SessionState, - request: api::BusinessProfileCreate, + request: api::ProfileCreate, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, -) -> RouterResult { - let business_profile_new = admin::create_business_profile_from_merchant_account( - state, - merchant_account, - request, - key_store, - ) - .await?; +) -> RouterResult { + let business_profile_new = + admin::create_profile_from_merchant_account(state, merchant_account, request, key_store) + .await?; let profile_name = business_profile_new.profile_name.clone(); @@ -3382,14 +3378,14 @@ pub async fn create_and_insert_business_profile( #[cfg(feature = "olap")] #[async_trait::async_trait] -trait BusinessProfileCreateBridge { +trait ProfileCreateBridge { #[cfg(feature = "v1")] async fn create_domain_model_from_request( self, state: &SessionState, merchant_account: &domain::MerchantAccount, key: &domain::MerchantKeyStore, - ) -> RouterResult; + ) -> RouterResult; #[cfg(feature = "v2")] async fn create_domain_model_from_request( @@ -3397,19 +3393,19 @@ trait BusinessProfileCreateBridge { state: &SessionState, key: &domain::MerchantKeyStore, merchant_id: &id_type::MerchantId, - ) -> RouterResult; + ) -> RouterResult; } #[cfg(feature = "olap")] #[async_trait::async_trait] -impl BusinessProfileCreateBridge for api::BusinessProfileCreate { +impl ProfileCreateBridge for api::ProfileCreate { #[cfg(feature = "v1")] async fn create_domain_model_from_request( self, state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - ) -> RouterResult { + ) -> RouterResult { use common_utils::ext_traits::AsyncExt; if let Some(session_expiry) = &self.session_expiry { @@ -3464,76 +3460,74 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate { }) .transpose()?; - Ok(domain::BusinessProfile::from( - domain::BusinessProfileSetter { - profile_id, - merchant_id: merchant_account.get_id().clone(), - profile_name, - created_at: current_time, - modified_at: current_time, - return_url: self - .return_url - .map(|return_url| return_url.to_string()) - .or(merchant_account.return_url.clone()), - enable_payment_response_hash: self - .enable_payment_response_hash - .unwrap_or(merchant_account.enable_payment_response_hash), - payment_response_hash_key: Some(payment_response_hash_key), - redirect_to_merchant_with_http_post: self - .redirect_to_merchant_with_http_post - .unwrap_or(merchant_account.redirect_to_merchant_with_http_post), - webhook_details: webhook_details.or(merchant_account.webhook_details.clone()), - metadata: self.metadata, - routing_algorithm: None, - intent_fulfillment_time: self - .intent_fulfillment_time - .map(i64::from) - .or(merchant_account.intent_fulfillment_time) - .or(Some(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME)), - frm_routing_algorithm: self - .frm_routing_algorithm - .or(merchant_account.frm_routing_algorithm.clone()), - #[cfg(feature = "payouts")] - payout_routing_algorithm: self - .payout_routing_algorithm - .or(merchant_account.payout_routing_algorithm.clone()), - #[cfg(not(feature = "payouts"))] - payout_routing_algorithm: None, - is_recon_enabled: merchant_account.is_recon_enabled, - applepay_verified_domains: self.applepay_verified_domains, - payment_link_config, - session_expiry: self - .session_expiry - .map(i64::from) - .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)), - authentication_connector_details: self - .authentication_connector_details - .map(ForeignInto::foreign_into), - payout_link_config, - is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, - is_extended_card_info_enabled: None, - extended_card_info_config: None, - use_billing_as_payment_method_billing: self - .use_billing_as_payment_method_billing - .or(Some(true)), - collect_shipping_details_from_wallet_connector: self - .collect_shipping_details_from_wallet_connector - .or(Some(false)), - collect_billing_details_from_wallet_connector: self - .collect_billing_details_from_wallet_connector - .or(Some(false)), - outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers - .map(Into::into), - tax_connector_id: self.tax_connector_id, - is_tax_connector_enabled: self.is_tax_connector_enabled, - always_collect_billing_details_from_wallet_connector: self - .always_collect_billing_details_from_wallet_connector, - always_collect_shipping_details_from_wallet_connector: self - .always_collect_shipping_details_from_wallet_connector, - dynamic_routing_algorithm: None, - is_network_tokenization_enabled: self.is_network_tokenization_enabled, - }, - )) + Ok(domain::Profile::from(domain::ProfileSetter { + profile_id, + merchant_id: merchant_account.get_id().clone(), + profile_name, + created_at: current_time, + modified_at: current_time, + return_url: self + .return_url + .map(|return_url| return_url.to_string()) + .or(merchant_account.return_url.clone()), + enable_payment_response_hash: self + .enable_payment_response_hash + .unwrap_or(merchant_account.enable_payment_response_hash), + payment_response_hash_key: Some(payment_response_hash_key), + redirect_to_merchant_with_http_post: self + .redirect_to_merchant_with_http_post + .unwrap_or(merchant_account.redirect_to_merchant_with_http_post), + webhook_details: webhook_details.or(merchant_account.webhook_details.clone()), + metadata: self.metadata, + routing_algorithm: None, + intent_fulfillment_time: self + .intent_fulfillment_time + .map(i64::from) + .or(merchant_account.intent_fulfillment_time) + .or(Some(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME)), + frm_routing_algorithm: self + .frm_routing_algorithm + .or(merchant_account.frm_routing_algorithm.clone()), + #[cfg(feature = "payouts")] + payout_routing_algorithm: self + .payout_routing_algorithm + .or(merchant_account.payout_routing_algorithm.clone()), + #[cfg(not(feature = "payouts"))] + payout_routing_algorithm: None, + is_recon_enabled: merchant_account.is_recon_enabled, + applepay_verified_domains: self.applepay_verified_domains, + payment_link_config, + session_expiry: self + .session_expiry + .map(i64::from) + .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)), + authentication_connector_details: self + .authentication_connector_details + .map(ForeignInto::foreign_into), + payout_link_config, + is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + use_billing_as_payment_method_billing: self + .use_billing_as_payment_method_billing + .or(Some(true)), + collect_shipping_details_from_wallet_connector: self + .collect_shipping_details_from_wallet_connector + .or(Some(false)), + collect_billing_details_from_wallet_connector: self + .collect_billing_details_from_wallet_connector + .or(Some(false)), + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers + .map(Into::into), + tax_connector_id: self.tax_connector_id, + is_tax_connector_enabled: self.is_tax_connector_enabled, + always_collect_billing_details_from_wallet_connector: self + .always_collect_billing_details_from_wallet_connector, + always_collect_shipping_details_from_wallet_connector: self + .always_collect_shipping_details_from_wallet_connector, + dynamic_routing_algorithm: None, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, + })) } #[cfg(feature = "v2")] @@ -3542,7 +3536,7 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate { state: &SessionState, key_store: &domain::MerchantKeyStore, merchant_id: &id_type::MerchantId, - ) -> RouterResult { + ) -> RouterResult { if let Some(session_expiry) = &self.session_expiry { helpers::validate_session_expiry(session_expiry.to_owned())?; } @@ -3581,74 +3575,72 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate { }) .transpose()?; - Ok(domain::BusinessProfile::from( - domain::BusinessProfileSetter { - id: profile_id, - merchant_id: merchant_id.clone(), - profile_name, - created_at: current_time, - modified_at: current_time, - return_url: self.return_url.map(|return_url| return_url.to_string()), - enable_payment_response_hash: self.enable_payment_response_hash.unwrap_or(true), - payment_response_hash_key: Some(payment_response_hash_key), - redirect_to_merchant_with_http_post: self - .redirect_to_merchant_with_http_post - .unwrap_or(true), - webhook_details, - metadata: self.metadata, - is_recon_enabled: false, - applepay_verified_domains: self.applepay_verified_domains, - payment_link_config, - session_expiry: self - .session_expiry - .map(i64::from) - .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)), - authentication_connector_details: self - .authentication_connector_details - .map(ForeignInto::foreign_into), - payout_link_config, - is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, - is_extended_card_info_enabled: None, - extended_card_info_config: None, - use_billing_as_payment_method_billing: self - .use_billing_as_payment_method_billing - .or(Some(true)), - collect_shipping_details_from_wallet_connector: self - .collect_shipping_details_from_wallet_connector_if_required - .or(Some(false)), - collect_billing_details_from_wallet_connector: self - .collect_billing_details_from_wallet_connector_if_required - .or(Some(false)), - outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers - .map(Into::into), - always_collect_billing_details_from_wallet_connector: self - .always_collect_billing_details_from_wallet_connector, - always_collect_shipping_details_from_wallet_connector: self - .always_collect_shipping_details_from_wallet_connector, - routing_algorithm_id: None, - frm_routing_algorithm_id: None, - payout_routing_algorithm_id: None, - order_fulfillment_time: self - .order_fulfillment_time - .map(|order_fulfillment_time| order_fulfillment_time.into_inner()) - .or(Some(common_utils::consts::DEFAULT_ORDER_FULFILLMENT_TIME)), - order_fulfillment_time_origin: self.order_fulfillment_time_origin, - default_fallback_routing: None, - tax_connector_id: self.tax_connector_id, - is_tax_connector_enabled: self.is_tax_connector_enabled, - is_network_tokenization_enabled: self.is_network_tokenization_enabled, - }, - )) + Ok(domain::Profile::from(domain::ProfileSetter { + id: profile_id, + merchant_id: merchant_id.clone(), + profile_name, + created_at: current_time, + modified_at: current_time, + return_url: self.return_url.map(|return_url| return_url.to_string()), + enable_payment_response_hash: self.enable_payment_response_hash.unwrap_or(true), + payment_response_hash_key: Some(payment_response_hash_key), + redirect_to_merchant_with_http_post: self + .redirect_to_merchant_with_http_post + .unwrap_or(true), + webhook_details, + metadata: self.metadata, + is_recon_enabled: false, + applepay_verified_domains: self.applepay_verified_domains, + payment_link_config, + session_expiry: self + .session_expiry + .map(i64::from) + .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)), + authentication_connector_details: self + .authentication_connector_details + .map(ForeignInto::foreign_into), + payout_link_config, + is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + use_billing_as_payment_method_billing: self + .use_billing_as_payment_method_billing + .or(Some(true)), + collect_shipping_details_from_wallet_connector: self + .collect_shipping_details_from_wallet_connector_if_required + .or(Some(false)), + collect_billing_details_from_wallet_connector: self + .collect_billing_details_from_wallet_connector_if_required + .or(Some(false)), + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers + .map(Into::into), + always_collect_billing_details_from_wallet_connector: self + .always_collect_billing_details_from_wallet_connector, + always_collect_shipping_details_from_wallet_connector: self + .always_collect_shipping_details_from_wallet_connector, + routing_algorithm_id: None, + frm_routing_algorithm_id: None, + payout_routing_algorithm_id: None, + order_fulfillment_time: self + .order_fulfillment_time + .map(|order_fulfillment_time| order_fulfillment_time.into_inner()) + .or(Some(common_utils::consts::DEFAULT_ORDER_FULFILLMENT_TIME)), + order_fulfillment_time_origin: self.order_fulfillment_time_origin, + default_fallback_routing: None, + tax_connector_id: self.tax_connector_id, + is_tax_connector_enabled: self.is_tax_connector_enabled, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, + })) } } #[cfg(feature = "olap")] -pub async fn create_business_profile( +pub async fn create_profile( state: SessionState, - request: api::BusinessProfileCreate, + request: api::ProfileCreate, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, -) -> RouterResponse { +) -> RouterResponse { let db = state.store.as_ref(); let key_manager_state = &(&state).into(); @@ -3689,17 +3681,18 @@ pub async fn create_business_profile( } Ok(service_api::ApplicationResponse::Json( - api_models::admin::BusinessProfileResponse::foreign_try_from(business_profile) + api_models::admin::ProfileResponse::foreign_try_from(business_profile) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details")?, )) } -pub async fn list_business_profile( +#[cfg(feature = "olap")] +pub async fn list_profile( state: SessionState, merchant_id: id_type::MerchantId, profile_id_list: Option>, -) -> RouterResponse> { +) -> RouterResponse> { let db = state.store.as_ref(); let key_store = db .get_merchant_key_store_by_merchant_id( @@ -3710,54 +3703,53 @@ pub async fn list_business_profile( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let profiles = db - .list_business_profile_by_merchant_id(&(&state).into(), &key_store, &merchant_id) + .list_profile_by_merchant_id(&(&state).into(), &key_store, &merchant_id) .await .to_not_found_response(errors::ApiErrorResponse::InternalServerError)? .clone(); let profiles = core_utils::filter_objects_based_on_profile_id_list(profile_id_list, profiles); let mut business_profiles = Vec::new(); for profile in profiles { - let business_profile = - api_models::admin::BusinessProfileResponse::foreign_try_from(profile) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to parse business profile details")?; + let business_profile = api_models::admin::ProfileResponse::foreign_try_from(profile) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse business profile details")?; business_profiles.push(business_profile); } Ok(service_api::ApplicationResponse::Json(business_profiles)) } -pub async fn retrieve_business_profile( +pub async fn retrieve_profile( state: SessionState, profile_id: id_type::ProfileId, key_store: domain::MerchantKeyStore, -) -> RouterResponse { +) -> RouterResponse { let db = state.store.as_ref(); let business_profile = db .find_business_profile_by_profile_id(&(&state).into(), &key_store, &profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; Ok(service_api::ApplicationResponse::Json( - api_models::admin::BusinessProfileResponse::foreign_try_from(business_profile) + api_models::admin::ProfileResponse::foreign_try_from(business_profile) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details")?, )) } -pub async fn delete_business_profile( +pub async fn delete_profile( state: SessionState, profile_id: id_type::ProfileId, merchant_id: &id_type::MerchantId, ) -> RouterResponse { let db = state.store.as_ref(); let delete_result = db - .delete_business_profile_by_profile_id_merchant_id(&profile_id, merchant_id) + .delete_profile_by_profile_id_merchant_id(&profile_id, merchant_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -3766,22 +3758,22 @@ pub async fn delete_business_profile( #[cfg(feature = "olap")] #[async_trait::async_trait] -trait BusinessProfileUpdateBridge { - async fn get_update_business_profile_object( +trait ProfileUpdateBridge { + async fn get_update_profile_object( self, state: &SessionState, key_store: &domain::MerchantKeyStore, - ) -> RouterResult; + ) -> RouterResult; } #[cfg(all(feature = "olap", feature = "v1"))] #[async_trait::async_trait] -impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { - async fn get_update_business_profile_object( +impl ProfileUpdateBridge for api::ProfileUpdate { + async fn get_update_profile_object( self, state: &SessionState, key_store: &domain::MerchantKeyStore, - ) -> RouterResult { + ) -> RouterResult { if let Some(session_expiry) = &self.session_expiry { helpers::validate_session_expiry(session_expiry.to_owned())?; } @@ -3842,8 +3834,8 @@ impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { }) .transpose()?; - Ok(domain::BusinessProfileUpdate::Update(Box::new( - domain::BusinessProfileGeneralUpdate { + Ok(domain::ProfileUpdate::Update(Box::new( + domain::ProfileGeneralUpdate { profile_name: self.profile_name, return_url: self.return_url.map(|return_url| return_url.to_string()), enable_payment_response_hash: self.enable_payment_response_hash, @@ -3889,12 +3881,12 @@ impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { #[cfg(all(feature = "olap", feature = "v2"))] #[async_trait::async_trait] -impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { - async fn get_update_business_profile_object( +impl ProfileUpdateBridge for api::ProfileUpdate { + async fn get_update_profile_object( self, state: &SessionState, key_store: &domain::MerchantKeyStore, - ) -> RouterResult { + ) -> RouterResult { if let Some(session_expiry) = &self.session_expiry { helpers::validate_session_expiry(session_expiry.to_owned())?; } @@ -3941,8 +3933,8 @@ impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { }) .transpose()?; - Ok(domain::BusinessProfileUpdate::Update(Box::new( - domain::BusinessProfileGeneralUpdate { + Ok(domain::ProfileUpdate::Update(Box::new( + domain::ProfileGeneralUpdate { profile_name: self.profile_name, return_url: self.return_url.map(|return_url| return_url.to_string()), enable_payment_response_hash: self.enable_payment_response_hash, @@ -3981,40 +3973,40 @@ impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { } #[cfg(feature = "olap")] -pub async fn update_business_profile( +pub async fn update_profile( state: SessionState, profile_id: &id_type::ProfileId, key_store: domain::MerchantKeyStore, - request: api::BusinessProfileUpdate, -) -> RouterResponse { + request: api::ProfileUpdate, +) -> RouterResponse { let db = state.store.as_ref(); let key_manager_state = &(&state).into(); let business_profile = db .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; - let business_profile_update = request - .get_update_business_profile_object(&state, &key_store) + let profile_update = request + .get_update_profile_object(&state, &key_store) .await?; let updated_business_profile = db - .update_business_profile_by_profile_id( + .update_profile_by_profile_id( key_manager_state, &key_store, business_profile, - business_profile_update, + profile_update, ) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; Ok(service_api::ApplicationResponse::Json( - api_models::admin::BusinessProfileResponse::foreign_try_from(updated_business_profile) + api_models::admin::ProfileResponse::foreign_try_from(updated_business_profile) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details")?, )) @@ -4022,13 +4014,13 @@ pub async fn update_business_profile( #[cfg(feature = "v2")] #[derive(Clone, Debug)] -pub struct BusinessProfileWrapper { - pub profile: domain::BusinessProfile, +pub struct ProfileWrapper { + pub profile: domain::Profile, } #[cfg(feature = "v2")] -impl BusinessProfileWrapper { - pub fn new(profile: domain::BusinessProfile) -> Self { +impl ProfileWrapper { + pub fn new(profile: domain::Profile) -> Self { Self { profile } } fn get_routing_config_cache_key(self) -> storage_impl::redis::cache::CacheKind<'static> { @@ -4046,7 +4038,7 @@ impl BusinessProfileWrapper { ) } - pub async fn update_business_profile_and_invalidate_routing_config_for_active_algorithm_id_update( + pub async fn update_profile_and_invalidate_routing_config_for_active_algorithm_id_update( self, db: &dyn StorageInterface, key_manager_state: &KeyManagerState, @@ -4062,18 +4054,18 @@ impl BusinessProfileWrapper { storage::enums::TransactionType::Payout => (None, Some(algorithm_id)), }; - let business_profile_update = domain::BusinessProfileUpdate::RoutingAlgorithmUpdate { + let profile_update = domain::ProfileUpdate::RoutingAlgorithmUpdate { routing_algorithm_id, payout_routing_algorithm_id, }; let profile = self.profile; - db.update_business_profile_by_profile_id( + db.update_profile_by_profile_id( key_manager_state, merchant_key_store, profile, - business_profile_update, + profile_update, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -4141,15 +4133,15 @@ impl BusinessProfileWrapper { .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to convert routing ref to value")?, ); - let business_profile_update = domain::BusinessProfileUpdate::DefaultRoutingFallbackUpdate { + let profile_update = domain::ProfileUpdate::DefaultRoutingFallbackUpdate { default_fallback_routing: Some(default_fallback_routing), }; - db.update_business_profile_by_profile_id( + db.update_profile_by_profile_id( key_manager_state, merchant_key_store, self.profile, - business_profile_update, + profile_update, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -4180,7 +4172,7 @@ pub async fn extended_card_info_toggle( let business_profile = db .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -4189,18 +4181,18 @@ pub async fn extended_card_info_toggle( .is_extended_card_info_enabled .is_some_and(|existing_config| existing_config != ext_card_info_choice.enabled) { - let business_profile_update = domain::BusinessProfileUpdate::ExtendedCardInfoUpdate { + let profile_update = domain::ProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled: Some(ext_card_info_choice.enabled), }; - db.update_business_profile_by_profile_id( + db.update_profile_by_profile_id( key_manager_state, &key_store, business_profile, - business_profile_update, + profile_update, ) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; } @@ -4230,7 +4222,7 @@ pub async fn connector_agnostic_mit_toggle( let business_profile = db .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -4243,18 +4235,18 @@ pub async fn connector_agnostic_mit_toggle( if business_profile.is_connector_agnostic_mit_enabled != Some(connector_agnostic_mit_choice.enabled) { - let business_profile_update = domain::BusinessProfileUpdate::ConnectorAgnosticMitUpdate { + let profile_update = domain::ProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled: Some(connector_agnostic_mit_choice.enabled), }; - db.update_business_profile_by_profile_id( + db.update_profile_by_profile_id( key_manager_state, &key_store, business_profile, - business_profile_update, + profile_update, ) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; } diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index f77296d7bc1..6f1746b5277 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -80,7 +80,7 @@ pub async fn perform_authentication( pub async fn perform_post_authentication( state: &SessionState, key_store: &domain::MerchantKeyStore, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, authentication_id: String, ) -> CustomResult { let (authentication_connector, three_ds_connector_account) = @@ -121,7 +121,7 @@ pub async fn perform_pre_authentication( key_store: &domain::MerchantKeyStore, card_number: cards::CardNumber, token: String, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, acquirer_details: Option, payment_id: Option, ) -> CustomResult { diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 4a1298bb7bf..3372d9b355e 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -75,7 +75,7 @@ pub fn construct_authentication_router_data( pub fn construct_post_authentication_router_data( authentication_connector: String, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, merchant_connector_account: payments_helpers::MerchantConnectorAccountType, authentication_data: &storage::Authentication, ) -> RouterResult { diff --git a/crates/router/src/core/authentication/utils.rs b/crates/router/src/core/authentication/utils.rs index b3ef8e2d9cc..e1e5dc9f624 100644 --- a/crates/router/src/core/authentication/utils.rs +++ b/crates/router/src/core/authentication/utils.rs @@ -266,7 +266,7 @@ where pub async fn get_authentication_connector_data( state: &SessionState, key_store: &domain::MerchantKeyStore, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( common_enums::AuthenticationConnectors, payments::helpers::MerchantConnectorAccountType, diff --git a/crates/router/src/core/mandate/helpers.rs b/crates/router/src/core/mandate/helpers.rs index 28ee784f6fe..b85bf6361f3 100644 --- a/crates/router/src/core/mandate/helpers.rs +++ b/crates/router/src/core/mandate/helpers.rs @@ -33,7 +33,7 @@ pub async fn get_profile_id_for_mandate( let profile_id = pi.profile_id .clone() - .ok_or(errors::ApiErrorResponse::BusinessProfileNotFound { + .ok_or(errors::ApiErrorResponse::ProfileNotFound { id: pi .profile_id .map(|profile_id| profile_id.get_string_repr().to_owned()) diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 0065e5a32b9..100e7abc85e 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -140,7 +140,7 @@ pub async fn form_payment_link_data( let business_profile = db .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -762,7 +762,7 @@ pub async fn get_payment_link_status( let business_profile = db .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index fa7ddfce997..8e3d2799fd4 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -88,7 +88,7 @@ pub async fn retrieve_payment_method( payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, merchant_key_store: &domain::MerchantKeyStore, - business_profile: Option<&domain::BusinessProfile>, + business_profile: Option<&domain::Profile>, ) -> RouterResult<(Option, Option)> { match pm_data { pm_opt @ Some(pm @ domain::PaymentMethodData::Card(_)) => { @@ -507,7 +507,7 @@ pub async fn retrieve_payment_method_with_token( _storage_scheme: common_enums::enums::MerchantStorageScheme, _mandate_id: Option, _payment_method_info: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult { todo!() } @@ -528,7 +528,7 @@ pub async fn retrieve_payment_method_with_token( storage_scheme: common_enums::enums::MerchantStorageScheme, mandate_id: Option, payment_method_info: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult { let token = match token_data { storage::PaymentTokenData::TemporaryGeneric(generic_token) => { diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 79d1226f13a..d8ad8dbead4 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -89,7 +89,7 @@ use crate::{ services, types::{ api::{self, routing as routing_types, PaymentMethodCreateExt}, - domain::{self, BusinessProfile}, + domain::{self, Profile}, storage::{self, enums, PaymentMethodListContext, PaymentTokenData}, transformers::ForeignTryFrom, }, @@ -3836,7 +3836,7 @@ pub async fn list_payment_methods( fn should_collect_shipping_or_billing_details_from_wallet_connector( payment_method: &api_enums::PaymentMethod, payment_experience_optional: Option<&api_enums::PaymentExperience>, - business_profile: Option<&BusinessProfile>, + business_profile: Option<&Profile>, mut required_fields_hs: HashMap, ) -> HashMap { match (payment_method, payment_experience_optional) { @@ -3908,7 +3908,7 @@ pub async fn call_surcharge_decision_management( state: routes::SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - business_profile: &BusinessProfile, + business_profile: &Profile, payment_attempt: &storage::PaymentAttempt, payment_intent: storage::PaymentIntent, billing_address: Option, @@ -3967,7 +3967,7 @@ pub async fn call_surcharge_decision_management_for_saved_card( state: &routes::SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - business_profile: &BusinessProfile, + business_profile: &Profile, payment_attempt: &storage::PaymentAttempt, payment_intent: storage::PaymentIntent, customer_payment_method_response: &mut api::CustomerPaymentMethodsListResponse, @@ -4713,7 +4713,7 @@ async fn perform_surcharge_ops( state: &routes::SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, - business_profile: Option, + business_profile: Option, response: &mut api::CustomerPaymentMethodsListResponse, ) -> Result<(), error_stack::Report> { let payment_attempt = payment_intent @@ -4758,7 +4758,7 @@ pub async fn perform_surcharge_ops( _state: &routes::SessionState, _merchant_account: domain::MerchantAccount, _key_store: domain::MerchantKeyStore, - _business_profile: Option, + _business_profile: Option, _response: &mut api::CustomerPaymentMethodsListResponse, ) -> Result<(), error_stack::Report> { todo!() diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b8255cfcfe5..85ae8641373 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -645,7 +645,7 @@ where pub async fn call_decision_manager( state: &SessionState, merchant_account: &domain::MerchantAccount, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, payment_data: &D, ) -> RouterResult> where @@ -694,7 +694,7 @@ where pub async fn call_decision_manager( state: &SessionState, merchant_account: &domain::MerchantAccount, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, payment_data: &D, ) -> RouterResult> where @@ -790,7 +790,7 @@ pub fn get_connector_data( pub async fn call_surcharge_decision_management_for_session_flow( state: &SessionState, _merchant_account: &domain::MerchantAccount, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, payment_attempt: &storage::PaymentAttempt, payment_intent: &storage::PaymentIntent, billing_address: Option, @@ -1095,7 +1095,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { .store .find_business_profile_by_profile_id(key_manager_state, &merchant_key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; Ok(router_types::RedirectPaymentFlowResponse { @@ -1232,7 +1232,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { .store .find_business_profile_by_profile_id(key_manager_state, &merchant_key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; Ok(router_types::RedirectPaymentFlowResponse { @@ -1467,7 +1467,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { .store .find_business_profile_by_profile_id(key_manager_state, &merchant_key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; Ok(router_types::AuthenticatePaymentFlowResponse { @@ -1527,7 +1527,7 @@ pub async fn call_connector_service( schedule_time: Option, header_payload: HeaderPayload, frm_suggestion: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, is_retry_payment: bool, ) -> RouterResult<( RouterData, @@ -1899,7 +1899,7 @@ pub async fn call_multiple_connectors_service( mut payment_data: D, customer: &Option, session_surcharge_details: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, header_payload: HeaderPayload, ) -> RouterResult where @@ -2612,7 +2612,7 @@ pub async fn get_connector_tokenization_action_when_confirm_true( merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<(D, TokenizationAction)> where F: Send + Clone, @@ -2742,7 +2742,7 @@ pub async fn tokenize_in_router_when_confirm_false_or_external_authentication, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult where F: Send + Clone, @@ -3352,7 +3352,7 @@ pub async fn get_connector_choice( state: &SessionState, req: &Req, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: &mut D, eligible_connectors: Option>, @@ -3431,7 +3431,7 @@ where pub async fn connector_selection( state: &SessionState, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: &mut D, request_straight_through: Option, @@ -3504,7 +3504,7 @@ where pub async fn decide_connector( state: SessionState, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: &mut D, request_straight_through: Option, @@ -3524,7 +3524,7 @@ where pub async fn decide_connector( state: SessionState, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: &mut D, request_straight_through: Option, @@ -4393,7 +4393,7 @@ where pub async fn route_connector_v1_for_payments( state: &SessionState, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: &mut D, transaction_data: core_routing::PaymentsDslInput<'_>, @@ -4471,7 +4471,7 @@ where pub async fn route_connector_v1_for_payouts( state: &SessionState, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, transaction_data: &payouts::PayoutData, routing_data: &mut storage::RoutingData, @@ -4486,7 +4486,7 @@ pub async fn route_connector_v1_for_payouts( pub async fn route_connector_v1_for_payouts( state: &SessionState, merchant_account: &domain::MerchantAccount, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, transaction_data: &payouts::PayoutData, routing_data: &mut storage::RoutingData, @@ -4690,7 +4690,7 @@ pub async fn payment_external_authentication( .store .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) .await - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + .change_context(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 984b24ab785..245e8ae629b 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -71,7 +71,7 @@ pub trait Feature { connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, header_payload: api_models::payments::HeaderPayload, ) -> RouterResult where diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index e425abce336..4ede7c50854 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -64,7 +64,7 @@ impl Feature _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, _connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { Err(ApiErrorResponse::NotImplemented { diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 0a55cefb427..f4bf869daa6 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -97,7 +97,7 @@ impl Feature for types::PaymentsAu connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index a56c49611a6..fb6ed6f3ac7 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -64,7 +64,7 @@ impl Feature connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { metrics::PAYMENT_CANCEL_COUNT.add( diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index cb37a75813b..d7fea5d20e3 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -64,7 +64,7 @@ impl Feature connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index 212fb4bc240..35038695f0c 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -78,7 +78,7 @@ impl Feature connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 226852e4a3c..6452d279135 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -71,7 +71,7 @@ impl Feature, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 428d170633e..9c06cc7fd1a 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -67,7 +67,7 @@ impl Feature connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs index d2e65c8a638..d6e61f3c1ca 100644 --- a/crates/router/src/core/payments/flows/reject_flow.rs +++ b/crates/router/src/core/payments/flows/reject_flow.rs @@ -63,7 +63,7 @@ impl Feature _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, _connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { Err(ApiErrorResponse::NotImplemented { diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 54d8dc230c7..9dfd4482885 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -77,7 +77,7 @@ impl Feature for types::PaymentsSessio connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, _connector_request: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { metrics::SESSION_TOKEN_CREATED.add( @@ -169,7 +169,7 @@ async fn create_applepay_session_token( state: &routes::SessionState, router_data: &types::PaymentsSessionRouterData, connector: &api::ConnectorData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let delayed_response = is_session_response_delayed(state, connector); @@ -638,7 +638,7 @@ fn create_gpay_session_token( state: &routes::SessionState, router_data: &types::PaymentsSessionRouterData, connector: &api::ConnectorData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult { let connector_metadata = router_data.connector_meta_data.clone(); let delayed_response = is_session_response_delayed(state, connector); @@ -825,7 +825,7 @@ where connector: &api::ConnectorData, _confirm: Option, call_connector_action: payments::CallConnectorAction, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, header_payload: api_models::payments::HeaderPayload, ) -> RouterResult; } @@ -834,7 +834,7 @@ fn create_paypal_sdk_session_token( _state: &routes::SessionState, router_data: &types::PaymentsSessionRouterData, connector: &api::ConnectorData, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult { let connector_metadata = router_data.connector_meta_data.clone(); @@ -874,7 +874,7 @@ impl RouterDataSession for types::PaymentsSessionRouterData { connector: &api::ConnectorData, _confirm: Option, call_connector_action: payments::CallConnectorAction, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { match connector.get_token { diff --git a/crates/router/src/core/payments/flows/session_update_flow.rs b/crates/router/src/core/payments/flows/session_update_flow.rs index c12667e19a2..3ada522a61f 100644 --- a/crates/router/src/core/payments/flows/session_update_flow.rs +++ b/crates/router/src/core/payments/flows/session_update_flow.rs @@ -72,7 +72,7 @@ impl Feature connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index c412fd2b37f..f9a5458840e 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -69,7 +69,7 @@ impl Feature for types::Setup connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _header_payload: api_models::payments::HeaderPayload, ) -> RouterResult { let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 81d9130db85..e9dca13dc80 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1809,7 +1809,7 @@ pub async fn retrieve_card_with_permanent_token( _storage_scheme: enums::MerchantStorageScheme, mandate_id: Option, payment_method_info: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult { let customer_id = payment_intent .customer_id @@ -2121,7 +2121,7 @@ pub async fn make_pm_data<'a, F: Clone, R, D>( _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, _storage_scheme: common_enums::enums::MerchantStorageScheme, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: Option<&domain::Profile>, ) -> RouterResult<( BoxedOperation<'a, F, R, D>, Option, @@ -2141,7 +2141,7 @@ pub async fn make_pm_data<'a, F: Clone, R, D>( merchant_key_store: &domain::MerchantKeyStore, customer: &Option, storage_scheme: common_enums::enums::MerchantStorageScheme, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( BoxedOperation<'a, F, R, D>, Option, @@ -2250,7 +2250,7 @@ pub async fn store_in_vault_and_generate_ppmt( payment_attempt: &PaymentAttempt, payment_method: enums::PaymentMethod, merchant_key_store: &domain::MerchantKeyStore, - business_profile: Option<&domain::BusinessProfile>, + business_profile: Option<&domain::Profile>, ) -> RouterResult { let router_token = vault::Vault::store_payment_method_data_in_locker( state, @@ -2292,7 +2292,7 @@ pub async fn store_payment_method_data_in_vault( payment_method: enums::PaymentMethod, payment_method_data: &domain::PaymentMethodData, merchant_key_store: &domain::MerchantKeyStore, - business_profile: Option<&domain::BusinessProfile>, + business_profile: Option<&domain::Profile>, ) -> RouterResult> { if should_store_payment_method_data_in_vault( &state.conf.temp_locker_enable_config, @@ -2710,7 +2710,7 @@ pub(super) fn validate_payment_list_request_for_joins( pub fn get_handle_response_url( payment_id: id_type::PaymentId, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, response: &api::PaymentsResponse, connector: String, ) -> RouterResult { @@ -2731,7 +2731,7 @@ pub fn get_handle_response_url( } pub fn make_merchant_url_with_response( - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, redirection_response: api::PgRedirectResponse, request_return_url: Option<&String>, client_secret: Option<&masking::Secret>, @@ -2840,7 +2840,7 @@ pub fn make_pg_redirect_response( pub fn make_url_with_signature( redirect_url: &str, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult { let mut url = url::Url::parse(redirect_url) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -4546,7 +4546,7 @@ pub async fn get_apple_pay_retryable_connectors( key_store: &domain::MerchantKeyStore, pre_routing_connector_data_list: &[api::ConnectorData], merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, ) -> CustomResult>, errors::ApiErrorResponse> where F: Send + Clone, @@ -4632,7 +4632,7 @@ where .attach_printable("Failed to get merchant default fallback connectors config")?; #[cfg(feature = "v2")] - let fallback_connetors_list = core_admin::BusinessProfileWrapper::new(business_profile) + let fallback_connetors_list = core_admin::ProfileWrapper::new(business_profile) .get_default_fallback_list_of_connector_under_profile() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to get merchant default fallback connectors config")?; @@ -5187,7 +5187,7 @@ pub async fn get_payment_method_details_from_payment_token( payment_intent: &PaymentIntent, key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult> { let hyperswitch_token = if let Some(token) = payment_attempt.payment_token.clone() { let redis_conn = state @@ -5342,7 +5342,7 @@ pub enum PaymentExternalAuthenticationFlow { pub async fn get_payment_external_authentication_flow_during_confirm( state: &SessionState, key_store: &domain::MerchantKeyStore, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, payment_data: &mut PaymentData, connector_call_type: &api::ConnectorCallType, mandate_type: Option, diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 4a38fac8522..5199583eb5d 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -109,7 +109,7 @@ pub struct GetTrackerResponse<'a, F: Clone, R, D> { pub operation: BoxedOperation<'a, F, R, D>, pub customer_details: Option, pub payment_data: D, - pub business_profile: domain::BusinessProfile, + pub business_profile: domain::Profile, pub mandate_type: Option, } @@ -148,7 +148,7 @@ pub trait Domain: Send + Sync { storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( BoxedOperation<'a, F, R, D>, Option, @@ -190,7 +190,7 @@ pub trait Domain: Send + Sync { _payment_data: &mut D, _should_continue_confirm_transaction: &mut bool, _connector_call_type: &ConnectorCallType, - _merchant_account: &domain::BusinessProfile, + _merchant_account: &domain::Profile, _key_store: &domain::MerchantKeyStore, _mandate_type: Option, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -203,7 +203,7 @@ pub trait Domain: Send + Sync { _state: &SessionState, _payment_data: &mut D, _connector_call_type: &ConnectorCallType, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _key_store: &domain::MerchantKeyStore, _merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -225,7 +225,7 @@ pub trait Domain: Send + Sync { &'a self, _state: &SessionState, _payment_id: &common_utils::id_type::PaymentId, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, _payment_method_data: Option<&domain::PaymentMethodData>, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) @@ -274,7 +274,7 @@ pub trait PostUpdateTracker: Send { _merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, _payment_data: &mut D, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -371,7 +371,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest, D>, Option, @@ -465,7 +465,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>, Option, @@ -570,7 +570,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCancelRequest, D>, Option, @@ -634,7 +634,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRejectRequest, D>, Option, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 9c5b632eff0..c57f4c34458 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -85,7 +85,7 @@ impl GetTracker, api::PaymentsCaptureRequest> .store .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index d0a896bc204..f21ccb2c5e1 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -154,7 +154,7 @@ impl GetTracker, api::PaymentsCancelRequest> let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 1b1b29e9160..710d6dd7cec 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -205,7 +205,7 @@ impl GetTracker, api::PaymentsCaptu let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 0032aa2f6b3..99d8df2dfe9 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -293,7 +293,7 @@ impl GetTracker, api::PaymentsRequest> for Co let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -400,7 +400,7 @@ impl Domain> for Comple storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( CompleteAuthorizeOperation<'a, F>, Option, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 85056380753..c75cc29ce51 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -164,7 +164,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ) .map(|business_profile_result| { business_profile_result.to_not_found_response( - errors::ApiErrorResponse::BusinessProfileNotFound { + errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), }, ) @@ -776,7 +776,7 @@ impl Domain> for Paymen storage_scheme: storage_enums::MerchantStorageScheme, key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( PaymentConfirmOperation<'a, F>, Option, @@ -861,7 +861,7 @@ impl Domain> for Paymen payment_data: &mut PaymentData, should_continue_confirm_transaction: &mut bool, connector_call_type: &ConnectorCallType, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, mandate_type: Option, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -965,7 +965,7 @@ impl Domain> for Paymen &'a self, state: &SessionState, payment_id: &common_utils::id_type::PaymentId, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, payment_method_data: Option<&domain::PaymentMethodData>, ) -> CustomResult<(), errors::ApiErrorResponse> { if let (Some(true), Some(domain::PaymentMethodData::Card(card)), Some(merchant_config)) = ( diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index a9cd1ae4d84..0731def9453 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -133,11 +133,9 @@ impl GetTracker, api::PaymentsRequest> for Pa &profile_id, ) .await - .to_not_found_response( - errors::ApiErrorResponse::BusinessProfileNotFound { - id: profile_id.get_string_repr().to_owned(), - }, - )? + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { + id: profile_id.get_string_repr().to_owned(), + })? }; let customer_acceptance = request.customer_acceptance.clone().map(From::from); @@ -595,7 +593,7 @@ impl Domain> for Paymen state: &SessionState, payment_data: &mut PaymentData, _connector_call_type: &ConnectorCallType, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -702,7 +700,7 @@ impl Domain> for Paymen storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( PaymentCreateOperation<'a, F>, Option, diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index 3d67dadd34d..040eb92d459 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -140,7 +140,7 @@ impl GetTracker, PaymentsCancelRequest> for P .store .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index ae6ac330679..90ca49c9746 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -96,7 +96,7 @@ impl PostUpdateTracker, types::PaymentsAuthor merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -115,7 +115,7 @@ impl PostUpdateTracker, types::PaymentsAuthor merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -494,7 +494,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -789,7 +789,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, @@ -883,7 +883,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult<(), errors::ApiErrorResponse> where F: 'b + Clone + Send + Sync, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index f12cd71cdb0..0542c279a29 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -163,7 +163,7 @@ impl GetTracker, api::PaymentsSessionRequest> let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -330,7 +330,7 @@ where _storage_scheme: storage_enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( PaymentSessionOperation<'b, F>, Option, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index cd98d448c36..4b44933f6dd 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -148,7 +148,7 @@ impl GetTracker, api::PaymentsStartRequest> f let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -302,7 +302,7 @@ where storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( PaymentSessionOperation<'a, F>, Option, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 0760cd449fe..d28476b67b8 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -93,7 +93,7 @@ impl Domain> for Paymen _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( PaymentStatusOperation<'a, F, api::PaymentsRequest>, Option, @@ -408,7 +408,7 @@ async fn get_tracker_for_sync< let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index a1bfb47fd92..c5e7cebac34 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -424,7 +424,7 @@ impl GetTracker, api::PaymentsRequest> for Pa let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -522,7 +522,7 @@ impl Domain> for Paymen state: &SessionState, payment_data: &mut PaymentData, _connector_call_type: &ConnectorCallType, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -628,7 +628,7 @@ impl Domain> for Paymen storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<( PaymentUpdateOperation<'a, F>, Option, diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index c95fa22a039..4e020c788b6 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -120,7 +120,7 @@ impl .store .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -335,7 +335,7 @@ impl Domain, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( PaymentIncrementalAuthorizationOperation<'a, F>, Option, diff --git a/crates/router/src/core/payments/operations/tax_calculation.rs b/crates/router/src/core/payments/operations/tax_calculation.rs index b30d1c45467..246485c353a 100644 --- a/crates/router/src/core/payments/operations/tax_calculation.rs +++ b/crates/router/src/core/payments/operations/tax_calculation.rs @@ -124,7 +124,7 @@ impl GetTracker, api::PaymentsDynamicTaxCalcu let business_profile = db .find_business_profile_by_profile_id(key_manager_state, key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -217,7 +217,7 @@ impl Domain, _connector_call_type: &ConnectorCallType, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, merchant_account: &domain::MerchantAccount, ) -> errors::CustomResult<(), errors::ApiErrorResponse> { @@ -330,7 +330,7 @@ impl Domain, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<( PaymentSessionUpdateOperation<'a, F>, Option, diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index ce2456e08c4..64a85e3358d 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -43,7 +43,7 @@ pub async fn do_gsm_actions( validate_result: &operations::ValidateResult, schedule_time: Option, frm_suggestion: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult> where F: Clone + Send + Sync, @@ -286,7 +286,7 @@ pub async fn do_retry( schedule_time: Option, is_step_up: bool, frm_suggestion: Option, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult> where F: Clone + Send + Sync, diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 5143068db96..a3123e91a14 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -283,7 +283,7 @@ pub async fn perform_static_routing_v1( state: &SessionState, merchant_id: &common_utils::id_type::MerchantId, algorithm_id: Option<&common_utils::id_type::RoutingId>, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, transaction_data: &routing::TransactionData<'_>, ) -> RoutingResult> { let algorithm_id = if let Some(id) = algorithm_id { @@ -298,7 +298,7 @@ pub async fn perform_static_routing_v1( .await .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; #[cfg(feature = "v2")] - let fallback_config = admin::BusinessProfileWrapper::new(business_profile.clone()) + let fallback_config = admin::ProfileWrapper::new(business_profile.clone()) .get_default_fallback_list_of_connector_under_profile() .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; @@ -721,7 +721,7 @@ pub async fn perform_fallback_routing( key_store: &domain::MerchantKeyStore, transaction_data: &routing::TransactionData<'_>, eligible_connectors: Option<&Vec>, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RoutingResult> { #[cfg(feature = "v1")] let fallback_config = routing::helpers::get_merchant_default_config( @@ -744,7 +744,7 @@ pub async fn perform_fallback_routing( .await .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; #[cfg(feature = "v2")] - let fallback_config = admin::BusinessProfileWrapper::new(business_profile.clone()) + let fallback_config = admin::ProfileWrapper::new(business_profile.clone()) .get_default_fallback_list_of_connector_under_profile() .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; let backend_input = match transaction_data { @@ -771,7 +771,7 @@ pub async fn perform_eligibility_analysis_with_fallback( chosen: Vec, transaction_data: &routing::TransactionData<'_>, eligible_connectors: Option>, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RoutingResult> { let mut final_selection = perform_eligibility_analysis( state, @@ -1000,7 +1000,7 @@ pub async fn perform_session_flow_routing( async fn perform_session_routing_for_pm_type( session_pm_input: &SessionRoutingPmTypeInput<'_>, transaction_type: &api_enums::TransactionType, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RoutingResult>> { let merchant_id = &session_pm_input.key_store.merchant_id; @@ -1083,13 +1083,13 @@ async fn perform_session_routing_for_pm_type( async fn perform_session_routing_for_pm_type( session_pm_input: &SessionRoutingPmTypeInput<'_>, transaction_type: &api_enums::TransactionType, - business_profile: &domain::BusinessProfile, + 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::BusinessProfileWrapper::new(business_profile.clone()); + 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(), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 46e02e6b1a8..2d81aecb30e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -77,7 +77,7 @@ pub async fn save_payment_method( currency: Option, billing_name: Option>, payment_method_billing_address: Option<&api::Address>, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> RouterResult<(Option, Option)> where FData: mandate::MandateBehaviour + Clone, @@ -754,7 +754,7 @@ pub async fn save_payment_method( _currency: Option, _billing_name: Option>, _payment_method_billing_address: Option<&api::Address>, - _business_profile: &domain::BusinessProfile, + _business_profile: &domain::Profile, ) -> RouterResult<(Option, Option)> where FData: mandate::MandateBehaviour + Clone, diff --git a/crates/router/src/core/payments/types.rs b/crates/router/src/core/payments/types.rs index 2257bd3e01f..66c3eb91f51 100644 --- a/crates/router/src/core/payments/types.rs +++ b/crates/router/src/core/payments/types.rs @@ -19,7 +19,7 @@ use crate::{ core::errors::{self, RouterResult}, routes::SessionState, types::{ - domain::BusinessProfile, + domain::Profile, storage::{self, enums as storage_enums}, transformers::ForeignTryFrom, }, @@ -291,7 +291,7 @@ impl SurchargeMetadata { pub async fn persist_individual_surcharge_details_in_redis( &self, state: &SessionState, - business_profile: &BusinessProfile, + business_profile: &Profile, ) -> RouterResult<()> { if !self.is_empty_result() { let redis_conn = state diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index c160acb50c2..e40c0f353aa 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -62,7 +62,7 @@ use crate::{ #[derive(Clone)] pub struct PayoutData { pub billing_address: Option, - pub business_profile: domain::BusinessProfile, + pub business_profile: domain::Profile, pub customer_details: Option, pub merchant_connector_account: Option, pub payouts: storage::Payouts, @@ -2820,7 +2820,7 @@ async fn validate_and_get_business_profile( merchant_key_store: &domain::MerchantKeyStore, profile_id: &common_utils::id_type::ProfileId, merchant_id: &common_utils::id_type::MerchantId, -) -> RouterResult { +) -> RouterResult { let db = &*state.store; let key_manager_state = &state.into(); @@ -2837,7 +2837,7 @@ async fn validate_and_get_business_profile( } else { db.find_business_profile_by_profile_id(key_manager_state, merchant_key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), }) } @@ -2846,7 +2846,7 @@ async fn validate_and_get_business_profile( #[allow(clippy::too_many_arguments)] pub async fn create_payout_link( state: &SessionState, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, customer_id: &CustomerId, merchant_id: &common_utils::id_type::MerchantId, req: &payouts::PayoutCreateRequest, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index ab13fa4c02d..43412ebfd70 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -792,7 +792,7 @@ pub async fn decide_payout_connector( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; // 2. Check routing algorithm passed in the request if let Some(routing_algorithm) = request_straight_through { diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 2ce75065673..a1ba53bcd77 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -167,7 +167,7 @@ pub async fn create_routing_algorithm_under_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?; @@ -269,7 +269,7 @@ pub async fn create_routing_algorithm_under_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?; @@ -337,7 +337,7 @@ pub async fn link_routing_config_under_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; utils::when( routing_algorithm.0.algorithm_for != *transaction_type, @@ -360,8 +360,8 @@ pub async fn link_routing_config_under_profile( }) }, )?; - admin::BusinessProfileWrapper::new(business_profile) - .update_business_profile_and_invalidate_routing_config_for_active_algorithm_id_update( + admin::ProfileWrapper::new(business_profile) + .update_profile_and_invalidate_routing_config_for_active_algorithm_id_update( db, key_manager_state, &key_store, @@ -405,8 +405,8 @@ pub async fn link_routing_config( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + .get_required_value("Profile") + .change_context(errors::ApiErrorResponse::ProfileNotFound { id: routing_algorithm.profile_id.get_string_repr().to_owned(), })?; @@ -484,7 +484,7 @@ pub async fn link_routing_config( }, )?; routing_ref.update_algorithm_id(algorithm_id); - helpers::update_business_profile_active_algorithm_ref( + helpers::update_profile_active_algorithm_ref( db, key_manager_state, &key_store, @@ -525,7 +525,7 @@ pub async fn retrieve_routing_algorithm_from_algorithm_id( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") + .get_required_value("Profile") .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?; core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?; @@ -566,7 +566,7 @@ pub async fn retrieve_routing_algorithm_from_algorithm_id( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") + .get_required_value("Profile") .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?; core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?; @@ -599,7 +599,7 @@ pub async fn unlink_routing_config_under_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; let routing_algo_id = match transaction_type { enums::TransactionType::Payment => business_profile.routing_algorithm_id.clone(), @@ -615,8 +615,8 @@ pub async fn unlink_routing_config_under_profile( ) .await?; let response = record.0.foreign_into(); - admin::BusinessProfileWrapper::new(business_profile) - .update_business_profile_and_invalidate_routing_config_for_active_algorithm_id_update( + admin::ProfileWrapper::new(business_profile) + .update_profile_and_invalidate_routing_config_for_active_algorithm_id_update( db, key_manager_state, &key_store, @@ -701,7 +701,7 @@ pub async fn unlink_routing_config( .await .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; let response = record.foreign_into(); - helpers::update_business_profile_active_algorithm_ref( + helpers::update_profile_active_algorithm_ref( db, key_manager_state, &key_store, @@ -745,8 +745,8 @@ pub async fn update_default_fallback_routing( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; - let profile_wrapper = admin::BusinessProfileWrapper::new(profile); + .get_required_value("Profile")?; + let profile_wrapper = admin::ProfileWrapper::new(profile); let default_list_of_connectors = profile_wrapper.get_default_fallback_list_of_connector_under_profile()?; @@ -873,9 +873,9 @@ pub async fn retrieve_default_fallback_algorithm_for_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; - let connectors_choice = admin::BusinessProfileWrapper::new(profile) + let connectors_choice = admin::ProfileWrapper::new(profile) .get_default_fallback_list_of_connector_under_profile()?; metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); @@ -924,7 +924,7 @@ pub async fn retrieve_routing_config_under_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile")?; + .get_required_value("Profile")?; let record = db .list_routing_algorithm_metadata_by_profile_id( @@ -970,17 +970,13 @@ pub async fn retrieve_linked_routing_config( ) .await? .map(|profile| vec![profile]) - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + .get_required_value("Profile") + .change_context(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })? } else { let business_profile = db - .list_business_profile_by_merchant_id( - key_manager_state, - &key_store, - merchant_account.get_id(), - ) + .list_profile_by_merchant_id(key_manager_state, &key_store, merchant_account.get_id()) .await .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; core_utils::filter_objects_based_on_profile_id_list( @@ -1036,11 +1032,7 @@ pub async fn retrieve_default_routing_config_for_profiles( let key_manager_state = &(&state).into(); let all_profiles = db - .list_business_profile_by_merchant_id( - key_manager_state, - &key_store, - merchant_account.get_id(), - ) + .list_profile_by_merchant_id(key_manager_state, &key_store, merchant_account.get_id()) .await .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound) .attach_printable("error retrieving all business profiles for merchant")?; @@ -1097,8 +1089,8 @@ pub async fn update_default_routing_config_for_profile( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + .get_required_value("Profile") + .change_context(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; let default_config = helpers::get_merchant_default_config( @@ -1176,7 +1168,7 @@ pub async fn toggle_success_based_routing( let db = state.store.as_ref(); let key_manager_state = &(&state).into(); - let business_profile: domain::BusinessProfile = core_utils::validate_and_get_business_profile( + let business_profile: domain::Profile = core_utils::validate_and_get_business_profile( db, key_manager_state, &key_store, @@ -1184,8 +1176,8 @@ pub async fn toggle_success_based_routing( merchant_account.get_id(), ) .await? - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + .get_required_value("Profile") + .change_context(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index f818dc05f5b..f60a6589d5c 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -182,11 +182,11 @@ pub async fn update_merchant_active_algorithm_ref( } #[cfg(feature = "v1")] -pub async fn update_business_profile_active_algorithm_ref( +pub async fn update_profile_active_algorithm_ref( db: &dyn StorageInterface, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - current_business_profile: domain::BusinessProfile, + current_business_profile: domain::Profile, algorithm_id: routing_types::RoutingAlgorithmRef, transaction_type: &storage::enums::TransactionType, ) -> RouterResult<()> { @@ -214,12 +214,12 @@ pub async fn update_business_profile_active_algorithm_ref( storage::enums::TransactionType::Payout => (None, Some(ref_val)), }; - let business_profile_update = domain::BusinessProfileUpdate::RoutingAlgorithmUpdate { + let business_profile_update = domain::ProfileUpdate::RoutingAlgorithmUpdate { routing_algorithm, payout_routing_algorithm, }; - db.update_business_profile_by_profile_id( + db.update_profile_by_profile_id( key_manager_state, merchant_key_store, current_business_profile, @@ -241,17 +241,17 @@ pub async fn update_business_profile_active_dynamic_algorithm_ref( db: &dyn StorageInterface, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - current_business_profile: domain::BusinessProfile, + current_business_profile: domain::Profile, dynamic_routing_algorithm: routing_types::DynamicRoutingAlgorithmRef, ) -> RouterResult<()> { let ref_val = dynamic_routing_algorithm .encode_to_value() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to convert dynamic routing ref to value")?; - let business_profile_update = domain::BusinessProfileUpdate::DynamicRoutingAlgorithmUpdate { + let business_profile_update = domain::ProfileUpdate::DynamicRoutingAlgorithmUpdate { dynamic_routing_algorithm: Some(ref_val), }; - db.update_business_profile_by_profile_id( + db.update_profile_by_profile_id( key_manager_state, merchant_key_store, current_business_profile, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 89f44aa1524..4c797486970 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -2754,7 +2754,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account( let profiles = match role_info.get_entity_type() { EntityType::Organization | EntityType::Merchant | EntityType::Internal => state .store - .list_business_profile_by_merchant_id( + .list_profile_by_merchant_id( key_manager_state, &key_store, &user_from_token.merchant_id, @@ -2934,7 +2934,7 @@ pub async fn switch_merchant_for_user_in_org( let profile_id = state .store - .list_business_profile_by_merchant_id( + .list_profile_by_merchant_id( key_manager_state, &merchant_key_store, &request.merchant_id, @@ -2987,11 +2987,7 @@ pub async fn switch_merchant_for_user_in_org( let profile_id = state .store - .list_business_profile_by_merchant_id( - key_manager_state, - &merchant_key_store, - &merchant_id, - ) + .list_profile_by_merchant_id(key_manager_state, &merchant_key_store, &merchant_id) .await .change_context(UserErrors::InternalServerError) .attach_printable("Failed to list business profiles by merchant_id")? @@ -3047,7 +3043,7 @@ pub async fn switch_merchant_for_user_in_org( state .store - .list_business_profile_by_merchant_id( + .list_profile_by_merchant_id( key_manager_state, &merchant_key_store, &request.merchant_id, diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 0e8b21f3a8a..75630da14ce 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -1195,7 +1195,7 @@ pub async fn validate_and_get_business_profile( merchant_key_store: &domain::MerchantKeyStore, profile_id: Option<&common_utils::id_type::ProfileId>, merchant_id: &common_utils::id_type::MerchantId, -) -> RouterResult> { +) -> RouterResult> { profile_id .async_map(|profile_id| async { db.find_business_profile_by_profile_id( @@ -1204,7 +1204,7 @@ pub async fn validate_and_get_business_profile( profile_id, ) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), }) }) @@ -1303,7 +1303,7 @@ pub async fn get_profile_id_from_business_details( merchant_account.get_id(), ) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_name, })?; @@ -1508,7 +1508,7 @@ impl GetProfileId for diesel_models::routing_algorithm::RoutingProfileMetadata { } } -impl GetProfileId for domain::BusinessProfile { +impl GetProfileId for domain::Profile { fn get_profile_id(&self) -> Option<&common_utils::id_type::ProfileId> { Some(self.get_id()) } diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 19d5591d185..af531984a19 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -353,7 +353,7 @@ async fn incoming_webhooks_core( .store .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) .await - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; @@ -498,7 +498,7 @@ async fn payments_incoming_webhook_flow( state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, @@ -636,7 +636,7 @@ async fn payments_incoming_webhook_flow( async fn payouts_incoming_webhook_flow( state: SessionState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, event_type: webhooks::IncomingWebhookEvent, @@ -770,7 +770,7 @@ async fn payouts_incoming_webhook_flow( async fn refunds_incoming_webhook_flow( state: SessionState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, connector_name: &str, @@ -914,7 +914,7 @@ async fn get_or_update_dispute_object( organization_id: &common_utils::id_type::OrganizationId, payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, event_type: webhooks::IncomingWebhookEvent, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, connector_name: &str, ) -> CustomResult { let db = &*state.store; @@ -995,7 +995,7 @@ async fn external_authentication_incoming_webhook_flow( request_details: &IncomingWebhookRequestDetails<'_>, connector: &ConnectorEnum, object_ref_id: api::ObjectReferenceId, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, merchant_connector_account: domain::MerchantConnectorAccount, ) -> CustomResult { if source_verified { @@ -1161,7 +1161,7 @@ async fn external_authentication_incoming_webhook_flow( async fn mandates_incoming_webhook_flow( state: SessionState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, @@ -1254,7 +1254,7 @@ async fn frm_incoming_webhook_flow( source_verified: bool, event_type: webhooks::IncomingWebhookEvent, object_ref_id: api::ObjectReferenceId, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, ) -> CustomResult { if source_verified { let payment_attempt = @@ -1360,7 +1360,7 @@ async fn frm_incoming_webhook_flow( async fn disputes_incoming_webhook_flow( state: SessionState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, @@ -1433,7 +1433,7 @@ async fn bank_transfer_webhook_flow( state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, @@ -1695,6 +1695,7 @@ async fn fetch_optional_mca_and_connector( .attach_printable( "error while fetching merchant_connector_account from connector_id", )?; + #[cfg(feature = "v2")] let mca: domain::MerchantConnectorAccount = { let _ = merchant_account; diff --git a/crates/router/src/core/webhooks/outgoing.rs b/crates/router/src/core/webhooks/outgoing.rs index 7b6f703f6f7..aa2aae928c8 100644 --- a/crates/router/src/core/webhooks/outgoing.rs +++ b/crates/router/src/core/webhooks/outgoing.rs @@ -52,7 +52,7 @@ const OUTGOING_WEBHOOK_TIMEOUT_SECS: u64 = 5; pub(crate) async fn create_event_and_trigger_outgoing_webhook( state: SessionState, merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, merchant_key_store: &domain::MerchantKeyStore, event_type: enums::EventType, event_class: enums::EventClass, @@ -196,7 +196,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( #[instrument(skip_all)] pub(crate) async fn trigger_webhook_and_raise_event( state: SessionState, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, merchant_key_store: &domain::MerchantKeyStore, event: domain::Event, request_content: OutgoingWebhookRequestContent, @@ -236,7 +236,7 @@ pub(crate) async fn trigger_webhook_and_raise_event( async fn trigger_webhook_to_merchant( state: SessionState, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, merchant_key_store: &domain::MerchantKeyStore, event: domain::Event, request_content: OutgoingWebhookRequestContent, @@ -517,7 +517,7 @@ async fn raise_webhooks_analytics_event( pub(crate) async fn add_outgoing_webhook_retry_task_to_process_tracker( db: &dyn StorageInterface, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, event: &domain::Event, ) -> CustomResult { let schedule_time = outgoing_webhook_retry::get_webhook_delivery_retry_schedule_time( @@ -581,7 +581,7 @@ pub(crate) async fn add_outgoing_webhook_retry_task_to_process_tracker( } fn get_webhook_url_from_business_profile( - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult { let webhook_details = business_profile .webhook_details @@ -599,12 +599,12 @@ fn get_webhook_url_from_business_profile( pub(crate) fn get_outgoing_webhook_request( merchant_account: &domain::MerchantAccount, outgoing_webhook: api::OutgoingWebhook, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult { #[inline] fn get_outgoing_webhook_request_inner( outgoing_webhook: api::OutgoingWebhook, - business_profile: &domain::BusinessProfile, + business_profile: &domain::Profile, ) -> CustomResult { let mut headers = vec![( reqwest::header::CONTENT_TYPE.to_string(), diff --git a/crates/router/src/core/webhooks/webhook_events.rs b/crates/router/src/core/webhooks/webhook_events.rs index aa2dad3674b..34a7c69d91a 100644 --- a/crates/router/src/core/webhooks/webhook_events.rs +++ b/crates/router/src/core/webhooks/webhook_events.rs @@ -13,9 +13,9 @@ use crate::{ const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT: i64 = 100; #[derive(Debug)] -enum MerchantAccountOrBusinessProfile { +enum MerchantAccountOrProfile { MerchantAccount(domain::MerchantAccount), - BusinessProfile(domain::BusinessProfile), + Profile(domain::Profile), } #[instrument(skip(state))] @@ -36,14 +36,14 @@ pub async fn list_initial_delivery_attempts( let events = match constraints { api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => { match account { - MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => store + MerchantAccountOrProfile::MerchantAccount(merchant_account) => store .list_initial_events_by_merchant_id_primary_object_id(key_manager_state, merchant_account.get_id(), &object_id, &key_store, ) .await, - MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => store + MerchantAccountOrProfile::Profile(business_profile) => store .list_initial_events_by_profile_id_primary_object_id(key_manager_state, business_profile.get_id(), &object_id, @@ -73,7 +73,7 @@ pub async fn list_initial_delivery_attempts( }; match account { - MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => store + MerchantAccountOrProfile::MerchantAccount(merchant_account) => store .list_initial_events_by_merchant_id_constraints(key_manager_state, merchant_account.get_id(), created_after, @@ -83,7 +83,7 @@ pub async fn list_initial_delivery_attempts( &key_store, ) .await, - MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => store + MerchantAccountOrProfile::Profile(business_profile) => store .list_initial_events_by_profile_id_constraints(key_manager_state, business_profile.get_id(), created_after, @@ -266,7 +266,7 @@ async fn get_account_and_key_store( state: SessionState, merchant_id: common_utils::id_type::MerchantId, profile_id: Option, -) -> errors::RouterResult<(MerchantAccountOrBusinessProfile, domain::MerchantKeyStore)> { +) -> errors::RouterResult<(MerchantAccountOrProfile, domain::MerchantKeyStore)> { let store = state.store.as_ref(); let key_manager_state = &(&state).into(); let merchant_key_store = store @@ -297,12 +297,12 @@ async fn get_account_and_key_store( different than the merchant_id specified (`{merchant_id:?}`)." ) }) - .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { id: profile_id.get_string_repr().to_owned(), })?; Ok(( - MerchantAccountOrBusinessProfile::BusinessProfile(business_profile), + MerchantAccountOrProfile::Profile(business_profile), merchant_key_store, )) } @@ -318,7 +318,7 @@ async fn get_account_and_key_store( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; Ok(( - MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account), + MerchantAccountOrProfile::MerchantAccount(merchant_account), merchant_key_store, )) } diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index a789af1aa2f..81319ae4c0b 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -118,7 +118,7 @@ pub trait StorageInterface: + payment_link::PaymentLinkInterface + RedisConnInterface + RequestIdStore - + business_profile::BusinessProfileInterface + + business_profile::ProfileInterface + OrganizationInterface + routing_algorithm::RoutingAlgorithmInterface + gsm::GsmInterface diff --git a/crates/router/src/db/business_profile.rs b/crates/router/src/db/business_profile.rs index d2555f40c4f..61abb0d6fee 100644 --- a/crates/router/src/db/business_profile.rs +++ b/crates/router/src/db/business_profile.rs @@ -17,24 +17,23 @@ use crate::{ }; #[async_trait::async_trait] -pub trait BusinessProfileInterface +pub trait ProfileInterface where - domain::BusinessProfile: - Conversion, + domain::Profile: Conversion, { async fn insert_business_profile( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - business_profile: domain::BusinessProfile, - ) -> CustomResult; + business_profile: domain::Profile, + ) -> CustomResult; async fn find_business_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, profile_id: &common_utils::id_type::ProfileId, - ) -> CustomResult; + ) -> CustomResult; async fn find_business_profile_by_merchant_id_profile_id( &self, @@ -42,7 +41,7 @@ where merchant_key_store: &domain::MerchantKeyStore, merchant_id: &common_utils::id_type::MerchantId, profile_id: &common_utils::id_type::ProfileId, - ) -> CustomResult; + ) -> CustomResult; async fn find_business_profile_by_profile_name_merchant_id( &self, @@ -50,39 +49,39 @@ where merchant_key_store: &domain::MerchantKeyStore, profile_name: &str, merchant_id: &common_utils::id_type::MerchantId, - ) -> CustomResult; + ) -> CustomResult; - async fn update_business_profile_by_profile_id( + async fn update_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - current_state: domain::BusinessProfile, - business_profile_update: domain::BusinessProfileUpdate, - ) -> CustomResult; + current_state: domain::Profile, + profile_update: domain::ProfileUpdate, + ) -> CustomResult; - async fn delete_business_profile_by_profile_id_merchant_id( + async fn delete_profile_by_profile_id_merchant_id( &self, profile_id: &common_utils::id_type::ProfileId, merchant_id: &common_utils::id_type::MerchantId, ) -> CustomResult; - async fn list_business_profile_by_merchant_id( + async fn list_profile_by_merchant_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, merchant_id: &common_utils::id_type::MerchantId, - ) -> CustomResult, errors::StorageError>; + ) -> CustomResult, errors::StorageError>; } #[async_trait::async_trait] -impl BusinessProfileInterface for Store { +impl ProfileInterface for Store { #[instrument(skip_all)] async fn insert_business_profile( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - business_profile: domain::BusinessProfile, - ) -> CustomResult { + business_profile: domain::Profile, + ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; business_profile .construct_new() @@ -106,9 +105,9 @@ impl BusinessProfileInterface for Store { key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, profile_id: &common_utils::id_type::ProfileId, - ) -> CustomResult { + ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::BusinessProfile::find_by_profile_id(&conn, profile_id) + storage::Profile::find_by_profile_id(&conn, profile_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? .convert( @@ -126,9 +125,9 @@ impl BusinessProfileInterface for Store { merchant_key_store: &domain::MerchantKeyStore, merchant_id: &common_utils::id_type::MerchantId, profile_id: &common_utils::id_type::ProfileId, - ) -> CustomResult { + ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::BusinessProfile::find_by_merchant_id_profile_id(&conn, merchant_id, profile_id) + storage::Profile::find_by_merchant_id_profile_id(&conn, merchant_id, profile_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? .convert( @@ -147,9 +146,9 @@ impl BusinessProfileInterface for Store { merchant_key_store: &domain::MerchantKeyStore, profile_name: &str, merchant_id: &common_utils::id_type::MerchantId, - ) -> CustomResult { + ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::BusinessProfile::find_by_profile_name_merchant_id(&conn, profile_name, merchant_id) + storage::Profile::find_by_profile_name_merchant_id(&conn, profile_name, merchant_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? .convert( @@ -162,21 +161,18 @@ impl BusinessProfileInterface for Store { } #[instrument(skip_all)] - async fn update_business_profile_by_profile_id( + async fn update_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - current_state: domain::BusinessProfile, - business_profile_update: domain::BusinessProfileUpdate, - ) -> CustomResult { + current_state: domain::Profile, + profile_update: domain::ProfileUpdate, + ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; Conversion::convert(current_state) .await .change_context(errors::StorageError::EncryptionError)? - .update_by_profile_id( - &conn, - storage::BusinessProfileUpdateInternal::from(business_profile_update), - ) + .update_by_profile_id(&conn, storage::ProfileUpdateInternal::from(profile_update)) .await .map_err(|error| report!(errors::StorageError::from(error)))? .convert( @@ -189,26 +185,26 @@ impl BusinessProfileInterface for Store { } #[instrument(skip_all)] - async fn delete_business_profile_by_profile_id_merchant_id( + async fn delete_profile_by_profile_id_merchant_id( &self, profile_id: &common_utils::id_type::ProfileId, merchant_id: &common_utils::id_type::MerchantId, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::BusinessProfile::delete_by_profile_id_merchant_id(&conn, profile_id, merchant_id) + storage::Profile::delete_by_profile_id_merchant_id(&conn, profile_id, merchant_id) .await .map_err(|error| report!(errors::StorageError::from(error))) } #[instrument(skip_all)] - async fn list_business_profile_by_merchant_id( + async fn list_profile_by_merchant_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, merchant_id: &common_utils::id_type::MerchantId, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; - storage::BusinessProfile::list_business_profile_by_merchant_id(&conn, merchant_id) + storage::Profile::list_profile_by_merchant_id(&conn, merchant_id) .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|business_profiles| async { @@ -232,13 +228,13 @@ impl BusinessProfileInterface for Store { } #[async_trait::async_trait] -impl BusinessProfileInterface for MockDb { +impl ProfileInterface for MockDb { async fn insert_business_profile( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - business_profile: domain::BusinessProfile, - ) -> CustomResult { + business_profile: domain::Profile, + ) -> CustomResult { let stored_business_profile = Conversion::convert(business_profile) .await .change_context(errors::StorageError::EncryptionError)?; @@ -263,7 +259,7 @@ impl BusinessProfileInterface for MockDb { key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, profile_id: &common_utils::id_type::ProfileId, - ) -> CustomResult { + ) -> CustomResult { self.business_profiles .lock() .await @@ -296,7 +292,7 @@ impl BusinessProfileInterface for MockDb { merchant_key_store: &domain::MerchantKeyStore, merchant_id: &common_utils::id_type::MerchantId, profile_id: &common_utils::id_type::ProfileId, - ) -> CustomResult { + ) -> CustomResult { self.business_profiles .lock() .await @@ -326,13 +322,13 @@ impl BusinessProfileInterface for MockDb { ) } - async fn update_business_profile_by_profile_id( + async fn update_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - current_state: domain::BusinessProfile, - business_profile_update: domain::BusinessProfileUpdate, - ) -> CustomResult { + current_state: domain::Profile, + profile_update: domain::ProfileUpdate, + ) -> CustomResult { let profile_id = current_state.get_id().to_owned(); self.business_profiles .lock() @@ -340,16 +336,15 @@ impl BusinessProfileInterface for MockDb { .iter_mut() .find(|business_profile| business_profile.get_id() == current_state.get_id()) .async_map(|business_profile| async { - let business_profile_updated = - storage::BusinessProfileUpdateInternal::from(business_profile_update) - .apply_changeset( - Conversion::convert(current_state) - .await - .change_context(errors::StorageError::EncryptionError)?, - ); - *business_profile = business_profile_updated.clone(); + let profile_updated = storage::ProfileUpdateInternal::from(profile_update) + .apply_changeset( + Conversion::convert(current_state) + .await + .change_context(errors::StorageError::EncryptionError)?, + ); + *business_profile = profile_updated.clone(); - business_profile_updated + profile_updated .convert( key_manager_state, merchant_key_store.key.get_inner(), @@ -368,7 +363,7 @@ impl BusinessProfileInterface for MockDb { ) } - async fn delete_business_profile_by_profile_id_merchant_id( + async fn delete_profile_by_profile_id_merchant_id( &self, profile_id: &common_utils::id_type::ProfileId, merchant_id: &common_utils::id_type::MerchantId, @@ -387,12 +382,12 @@ impl BusinessProfileInterface for MockDb { Ok(true) } - async fn list_business_profile_by_merchant_id( + async fn list_profile_by_merchant_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, merchant_id: &common_utils::id_type::MerchantId, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult, errors::StorageError> { let business_profiles = self .business_profiles .lock() @@ -425,7 +420,7 @@ impl BusinessProfileInterface for MockDb { merchant_key_store: &domain::MerchantKeyStore, profile_name: &str, merchant_id: &common_utils::id_type::MerchantId, - ) -> CustomResult { + ) -> CustomResult { self.business_profiles .lock() .await diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 9b51946c5c4..ff7e8145c33 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -50,7 +50,7 @@ use crate::{ api_keys::ApiKeyInterface, authentication::AuthenticationInterface, authorization::AuthorizationInterface, - business_profile::BusinessProfileInterface, + business_profile::ProfileInterface, capture::CaptureInterface, cards_info::CardsInfoInterface, configs::ConfigInterface, @@ -2625,13 +2625,13 @@ impl MerchantKeyStoreInterface for KafkaStore { } #[async_trait::async_trait] -impl BusinessProfileInterface for KafkaStore { +impl ProfileInterface for KafkaStore { async fn insert_business_profile( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - business_profile: domain::BusinessProfile, - ) -> CustomResult { + business_profile: domain::Profile, + ) -> CustomResult { self.diesel_store .insert_business_profile(key_manager_state, merchant_key_store, business_profile) .await @@ -2642,7 +2642,7 @@ impl BusinessProfileInterface for KafkaStore { key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, profile_id: &id_type::ProfileId, - ) -> CustomResult { + ) -> CustomResult { self.diesel_store .find_business_profile_by_profile_id(key_manager_state, merchant_key_store, profile_id) .await @@ -2654,7 +2654,7 @@ impl BusinessProfileInterface for KafkaStore { merchant_key_store: &domain::MerchantKeyStore, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, - ) -> CustomResult { + ) -> CustomResult { self.diesel_store .find_business_profile_by_merchant_id_profile_id( key_manager_state, @@ -2665,15 +2665,15 @@ impl BusinessProfileInterface for KafkaStore { .await } - async fn update_business_profile_by_profile_id( + async fn update_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, - current_state: domain::BusinessProfile, - business_profile_update: domain::BusinessProfileUpdate, - ) -> CustomResult { + current_state: domain::Profile, + business_profile_update: domain::ProfileUpdate, + ) -> CustomResult { self.diesel_store - .update_business_profile_by_profile_id( + .update_profile_by_profile_id( key_manager_state, merchant_key_store, current_state, @@ -2682,28 +2682,24 @@ impl BusinessProfileInterface for KafkaStore { .await } - async fn delete_business_profile_by_profile_id_merchant_id( + async fn delete_profile_by_profile_id_merchant_id( &self, profile_id: &id_type::ProfileId, merchant_id: &id_type::MerchantId, ) -> CustomResult { self.diesel_store - .delete_business_profile_by_profile_id_merchant_id(profile_id, merchant_id) + .delete_profile_by_profile_id_merchant_id(profile_id, merchant_id) .await } - async fn list_business_profile_by_merchant_id( + async fn list_profile_by_merchant_id( &self, key_manager_state: &KeyManagerState, merchant_key_store: &domain::MerchantKeyStore, merchant_id: &id_type::MerchantId, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_business_profile_by_merchant_id( - key_manager_state, - merchant_key_store, - merchant_id, - ) + .list_profile_by_merchant_id(key_manager_state, merchant_key_store, merchant_id) .await } @@ -2713,7 +2709,7 @@ impl BusinessProfileInterface for KafkaStore { merchant_key_store: &domain::MerchantKeyStore, profile_name: &str, merchant_id: &id_type::MerchantId, - ) -> CustomResult { + ) -> CustomResult { self.diesel_store .find_business_profile_by_profile_name_merchant_id( key_manager_state, diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index caa96826937..b6ac439a5c4 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -121,8 +121,8 @@ pub fn mk_app( // This is a more specific route as compared to `MerchantConnectorAccount` // so it is registered before `MerchantConnectorAccount`. server_app = server_app - .service(routes::BusinessProfileNew::server(state.clone())) - .service(routes::BusinessProfile::server(state.clone())) + .service(routes::ProfileNew::server(state.clone())) + .service(routes::Profile::server(state.clone())) } server_app = server_app .service(routes::Payments::server(state.clone())) diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 482b9a1b9cc..8baed5089e8 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -38,6 +38,8 @@ pub mod payouts; #[cfg(any(feature = "olap", feature = "oltp"))] pub mod pm_auth; pub mod poll; +#[cfg(feature = "olap")] +pub mod profiles; #[cfg(feature = "recon")] pub mod recon; #[cfg(feature = "v1")] @@ -64,10 +66,10 @@ pub use self::app::Forex; #[cfg(all(feature = "olap", feature = "recon", feature = "v1"))] pub use self::app::Recon; pub use self::app::{ - ApiKeys, AppState, ApplePayCertificatesMigration, BusinessProfile, BusinessProfileNew, Cache, - Cards, Configs, ConnectorOnboarding, Customers, Disputes, EphemeralKey, Files, Gsm, Health, - Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, - Poll, Refunds, SessionState, User, Webhooks, + ApiKeys, AppState, ApplePayCertificatesMigration, Cache, Cards, Configs, ConnectorOnboarding, + Customers, Disputes, EphemeralKey, Files, Gsm, Health, Mandates, MerchantAccount, + MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll, Profile, ProfileNew, + Refunds, SessionState, User, Webhooks, }; #[cfg(feature = "olap")] pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents}; diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 8ba214f94b9..14711bef6a0 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -557,6 +557,7 @@ pub async fn connector_list_profile( ) .await } + /// Merchant Connector - Update /// /// To update an existing Merchant Connector. Helpful in enabling / disabling different payment methods and other settings for the connector etc. @@ -815,324 +816,6 @@ pub async fn merchant_account_toggle_all_kv( .await } -#[cfg(all(feature = "olap", feature = "v1"))] -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileCreate))] -pub async fn business_profile_create( - state: web::Data, - req: HttpRequest, - json_payload: web::Json, - path: web::Path, -) -> HttpResponse { - let flow = Flow::BusinessProfileCreate; - let payload = json_payload.into_inner(); - let merchant_id = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth_data, req, _| { - create_business_profile(state, req, auth_data.merchant_account, auth_data.key_store) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()), - &auth::JWTAuthMerchantFromRoute { - merchant_id, - required_permission: Permission::MerchantAccountWrite, - minimum_entity_level: EntityType::Merchant, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[cfg(all(feature = "olap", feature = "v2"))] -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileCreate))] -pub async fn business_profile_create( - state: web::Data, - req: HttpRequest, - json_payload: web::Json, -) -> HttpResponse { - let flow = Flow::BusinessProfileCreate; - let payload = json_payload.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth_data, req, _| { - create_business_profile(state, req, auth_data.merchant_account, auth_data.key_store) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromHeader, - &auth::JWTAuthMerchantFromHeader { - required_permission: Permission::MerchantAccountWrite, - minimum_entity_level: EntityType::Merchant, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[cfg(feature = "v1")] -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileRetrieve))] -pub async fn business_profile_retrieve( - state: web::Data, - req: HttpRequest, - path: web::Path<( - common_utils::id_type::MerchantId, - common_utils::id_type::ProfileId, - )>, -) -> HttpResponse { - let flow = Flow::BusinessProfileRetrieve; - let (merchant_id, profile_id) = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - profile_id, - |state, auth_data, profile_id, _| { - retrieve_business_profile(state, profile_id, auth_data.key_store) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()), - &auth::JWTAuthMerchantFromRoute { - merchant_id: merchant_id.clone(), - required_permission: Permission::MerchantAccountRead, - minimum_entity_level: EntityType::Profile, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[cfg(feature = "v2")] -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileRetrieve))] -pub async fn business_profile_retrieve( - state: web::Data, - req: HttpRequest, - path: web::Path, -) -> HttpResponse { - let flow = Flow::BusinessProfileRetrieve; - let profile_id = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - profile_id, - |state, auth_data, profile_id, _| { - retrieve_business_profile(state, profile_id, auth_data.key_store) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromHeader, - &auth::JWTAuthMerchantFromHeader { - required_permission: Permission::MerchantAccountRead, - minimum_entity_level: EntityType::Merchant, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[cfg(all(feature = "olap", feature = "v1"))] -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileUpdate))] -pub async fn business_profile_update( - state: web::Data, - req: HttpRequest, - path: web::Path<( - common_utils::id_type::MerchantId, - common_utils::id_type::ProfileId, - )>, - json_payload: web::Json, -) -> HttpResponse { - let flow = Flow::BusinessProfileUpdate; - let (merchant_id, profile_id) = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, auth_data, req, _| { - update_business_profile(state, &profile_id, auth_data.key_store, req) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()), - &auth::JWTAuthMerchantAndProfileFromRoute { - merchant_id: merchant_id.clone(), - profile_id: profile_id.clone(), - required_permission: Permission::MerchantAccountWrite, - minimum_entity_level: EntityType::Profile, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[cfg(feature = "v2")] -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileUpdate))] -pub async fn business_profile_update( - state: web::Data, - req: HttpRequest, - path: web::Path, - json_payload: web::Json, -) -> HttpResponse { - let flow = Flow::BusinessProfileUpdate; - let profile_id = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, auth_data, req, _| { - update_business_profile(state, &profile_id, auth_data.key_store, req) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromHeader, - &auth::JWTAuthMerchantFromHeader { - required_permission: Permission::MerchantAccountWrite, - minimum_entity_level: EntityType::Merchant, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileDelete))] -pub async fn business_profile_delete( - state: web::Data, - req: HttpRequest, - path: web::Path<( - common_utils::id_type::MerchantId, - common_utils::id_type::ProfileId, - )>, -) -> HttpResponse { - let flow = Flow::BusinessProfileDelete; - let (merchant_id, profile_id) = path.into_inner(); - - api::server_wrap( - flow, - state, - &req, - profile_id, - |state, _, profile_id, _| delete_business_profile(state, profile_id, &merchant_id), - &auth::AdminApiAuth, - api_locking::LockAction::NotApplicable, - ) - .await -} -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileList))] -pub async fn business_profiles_list( - state: web::Data, - req: HttpRequest, - path: web::Path, -) -> HttpResponse { - let flow = Flow::BusinessProfileList; - let merchant_id = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - merchant_id.clone(), - |state, _auth, merchant_id, _| list_business_profile(state, merchant_id, None), - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromHeader, - &auth::JWTAuthMerchantFromRoute { - merchant_id, - required_permission: Permission::MerchantAccountRead, - minimum_entity_level: EntityType::Merchant, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileList))] -pub async fn business_profiles_list_at_profile_level( - state: web::Data, - req: HttpRequest, - path: web::Path, -) -> HttpResponse { - let flow = Flow::BusinessProfileList; - let merchant_id = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - merchant_id.clone(), - |state, auth, merchant_id, _| { - list_business_profile( - state, - merchant_id, - auth.profile_id.map(|profile_id| vec![profile_id]), - ) - }, - auth::auth_type( - &auth::AdminApiAuthWithMerchantIdFromHeader, - &auth::JWTAuthMerchantFromRoute { - merchant_id, - required_permission: Permission::MerchantAccountRead, - minimum_entity_level: EntityType::Profile, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} - -#[instrument(skip_all, fields(flow = ?Flow::ToggleConnectorAgnosticMit))] -pub async fn toggle_connector_agnostic_mit( - state: web::Data, - req: HttpRequest, - path: web::Path<( - common_utils::id_type::MerchantId, - common_utils::id_type::ProfileId, - )>, - json_payload: web::Json, -) -> HttpResponse { - let flow = Flow::ToggleConnectorAgnosticMit; - let (merchant_id, profile_id) = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, _, req, _| connector_agnostic_mit_toggle(state, &merchant_id, &profile_id, req), - auth::auth_type( - &auth::HeaderAuth(auth::ApiKeyAuth), - &auth::JWTAuth { - permission: Permission::RoutingWrite, - minimum_entity_level: EntityType::Merchant, - }, - req.headers(), - ), - api_locking::LockAction::NotApplicable, - )) - .await -} /// Merchant Account - KV Status /// /// Toggle KV mode for the Merchant Account @@ -1178,28 +861,3 @@ pub async fn merchant_account_transfer_keys( )) .await } - -#[instrument(skip_all, fields(flow = ?Flow::ToggleExtendedCardInfo))] -pub async fn toggle_extended_card_info( - state: web::Data, - req: HttpRequest, - path: web::Path<( - common_utils::id_type::MerchantId, - common_utils::id_type::ProfileId, - )>, - json_payload: web::Json, -) -> HttpResponse { - let flow = Flow::ToggleExtendedCardInfo; - let (merchant_id, profile_id) = path.into_inner(); - - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, _, req, _| extended_card_info_toggle(state, &merchant_id, &profile_id, req), - &auth::AdminApiAuth, - api_locking::LockAction::NotApplicable, - )) - .await -} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 75b32976e1d..01a48c56401 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -51,8 +51,8 @@ use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_ve #[cfg(all(feature = "oltp", feature = "v1"))] use super::webhooks::*; use super::{ - admin, api_keys, cache::*, connector_onboarding, disputes, files, gsm, health::*, user, - user_role, + admin, api_keys, cache::*, connector_onboarding, disputes, files, gsm, health::*, profiles, + user, user_role, }; #[cfg(feature = "v1")] use super::{apple_pay_certificates_migration, blocklist, payment_link, webhook_events}; @@ -1244,8 +1244,7 @@ impl MerchantAccount { .route(web::put().to(admin::update_merchant_account)), ) .service( - web::resource("/profiles") - .route(web::get().to(admin::business_profiles_list)), + web::resource("/profiles").route(web::get().to(profiles::profiles_list)), ), ) } @@ -1601,19 +1600,19 @@ impl PayoutLink { } } -pub struct BusinessProfile; +pub struct Profile; #[cfg(all(feature = "olap", feature = "v2"))] -impl BusinessProfile { +impl Profile { pub fn server(state: AppState) -> Scope { web::scope("/v2/profiles") .app_data(web::Data::new(state)) - .service(web::resource("").route(web::post().to(admin::business_profile_create))) + .service(web::resource("").route(web::post().to(profiles::profile_create))) .service( web::scope("/{profile_id}") .service( web::resource("") - .route(web::get().to(admin::business_profile_retrieve)) - .route(web::put().to(admin::business_profile_update)), + .route(web::get().to(profiles::profile_retrieve)) + .route(web::put().to(profiles::profile_update)), ) .service( web::resource("/connector_accounts") @@ -1622,7 +1621,7 @@ impl BusinessProfile { .service( web::resource("/fallback_routing") .route(web::get().to(routing::routing_retrieve_default_config)) - .route(web::post().to(routing::routing_update_default_config)), + .route(web::patch().to(routing::routing_update_default_config)), ) .service( web::resource("/activate_routing_algorithm").route(web::patch().to( @@ -1664,14 +1663,14 @@ impl BusinessProfile { } } #[cfg(all(feature = "olap", feature = "v1"))] -impl BusinessProfile { +impl Profile { pub fn server(state: AppState) -> Scope { web::scope("/account/{account_id}/business_profile") .app_data(web::Data::new(state)) .service( web::resource("") - .route(web::post().to(admin::business_profile_create)) - .route(web::get().to(admin::business_profiles_list)), + .route(web::post().to(profiles::profile_create)) + .route(web::get().to(profiles::profiles_list)), ) .service( web::scope("/{profile_id}") @@ -1694,32 +1693,31 @@ impl BusinessProfile { ) .service( web::resource("") - .route(web::get().to(admin::business_profile_retrieve)) - .route(web::post().to(admin::business_profile_update)) - .route(web::delete().to(admin::business_profile_delete)), + .route(web::get().to(profiles::profile_retrieve)) + .route(web::post().to(profiles::profile_update)) + .route(web::delete().to(profiles::profile_delete)), ) .service( web::resource("/toggle_extended_card_info") - .route(web::post().to(admin::toggle_extended_card_info)), + .route(web::post().to(profiles::toggle_extended_card_info)), ) .service( web::resource("/toggle_connector_agnostic_mit") - .route(web::post().to(admin::toggle_connector_agnostic_mit)), + .route(web::post().to(profiles::toggle_connector_agnostic_mit)), ), ) } } -pub struct BusinessProfileNew; +pub struct ProfileNew; #[cfg(feature = "olap")] -impl BusinessProfileNew { +impl ProfileNew { pub fn server(state: AppState) -> Scope { web::scope("/account/{account_id}/profile") .app_data(web::Data::new(state)) .service( - web::resource("") - .route(web::get().to(admin::business_profiles_list_at_profile_level)), + web::resource("").route(web::get().to(profiles::profiles_list_at_profile_level)), ) .service( web::resource("/connectors").route(web::get().to(admin::connector_list_profile)), diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 774e6c75bb4..d6d3ec8a952 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -21,7 +21,7 @@ pub enum ApiIdentifier { CardsInfo, Files, Cache, - Business, + Profile, Verification, ApiKeys, PaymentLink, @@ -184,13 +184,13 @@ impl From for ApiIdentifier { Flow::CacheInvalidate => Self::Cache, - Flow::BusinessProfileCreate - | Flow::BusinessProfileUpdate - | Flow::BusinessProfileRetrieve - | Flow::BusinessProfileDelete - | Flow::BusinessProfileList + Flow::ProfileCreate + | Flow::ProfileUpdate + | Flow::ProfileRetrieve + | Flow::ProfileDelete + | Flow::ProfileList | Flow::ToggleExtendedCardInfo - | Flow::ToggleConnectorAgnosticMit => Self::Business, + | Flow::ToggleConnectorAgnosticMit => Self::Profile, Flow::PaymentLinkRetrieve | Flow::PaymentLinkInitiate diff --git a/crates/router/src/routes/profiles.rs b/crates/router/src/routes/profiles.rs new file mode 100644 index 00000000000..e254447cdef --- /dev/null +++ b/crates/router/src/routes/profiles.rs @@ -0,0 +1,381 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use common_enums::EntityType; +use router_env::{instrument, tracing, Flow}; + +use super::app::AppState; +use crate::{ + core::{admin::*, api_locking}, + services::{api, authentication as auth, authorization::permissions}, + types::api::admin, +}; + +#[cfg(all(feature = "olap", feature = "v1"))] +#[instrument(skip_all, fields(flow = ?Flow::ProfileCreate))] +pub async fn profile_create( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, + path: web::Path, +) -> HttpResponse { + let flow = Flow::ProfileCreate; + let payload = json_payload.into_inner(); + let merchant_id = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, auth_data, req, _| { + create_profile(state, req, auth_data.merchant_account, auth_data.key_store) + }, + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()), + &auth::JWTAuthMerchantFromRoute { + merchant_id, + required_permission: permissions::Permission::MerchantAccountWrite, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all(feature = "olap", feature = "v2"))] +#[instrument(skip_all, fields(flow = ?Flow::ProfileCreate))] +pub async fn profile_create( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ProfileCreate; + let payload = json_payload.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, auth_data, req, _| { + create_profile(state, req, auth_data.merchant_account, auth_data.key_store) + }, + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromHeader { + required_permission: permissions::Permission::MerchantAccountWrite, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "v1")] +#[instrument(skip_all, fields(flow = ?Flow::ProfileRetrieve))] +pub async fn profile_retrieve( + state: web::Data, + req: HttpRequest, + path: web::Path<( + common_utils::id_type::MerchantId, + common_utils::id_type::ProfileId, + )>, +) -> HttpResponse { + let flow = Flow::ProfileRetrieve; + let (merchant_id, profile_id) = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + profile_id, + |state, auth_data, profile_id, _| retrieve_profile(state, profile_id, auth_data.key_store), + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()), + &auth::JWTAuthMerchantFromRoute { + merchant_id: merchant_id.clone(), + required_permission: permissions::Permission::MerchantAccountRead, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "v2")] +#[instrument(skip_all, fields(flow = ?Flow::ProfileRetrieve))] +pub async fn profile_retrieve( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::ProfileRetrieve; + let profile_id = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + profile_id, + |state, auth_data, profile_id, _| retrieve_profile(state, profile_id, auth_data.key_store), + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromHeader { + required_permission: permissions::Permission::MerchantAccountRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all(feature = "olap", feature = "v1"))] +#[instrument(skip_all, fields(flow = ?Flow::ProfileUpdate))] +pub async fn profile_update( + state: web::Data, + req: HttpRequest, + path: web::Path<( + common_utils::id_type::MerchantId, + common_utils::id_type::ProfileId, + )>, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ProfileUpdate; + let (merchant_id, profile_id) = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth_data, req, _| update_profile(state, &profile_id, auth_data.key_store, req), + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromRoute(merchant_id.clone()), + &auth::JWTAuthMerchantAndProfileFromRoute { + merchant_id: merchant_id.clone(), + profile_id: profile_id.clone(), + required_permission: permissions::Permission::MerchantAccountWrite, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "v2")] +#[instrument(skip_all, fields(flow = ?Flow::ProfileUpdate))] +pub async fn profile_update( + state: web::Data, + req: HttpRequest, + path: web::Path, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ProfileUpdate; + let profile_id = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth_data, req, _| update_profile(state, &profile_id, auth_data.key_store, req), + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromHeader { + required_permission: permissions::Permission::MerchantAccountWrite, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::ProfileDelete))] +pub async fn profile_delete( + state: web::Data, + req: HttpRequest, + path: web::Path<( + common_utils::id_type::MerchantId, + common_utils::id_type::ProfileId, + )>, +) -> HttpResponse { + let flow = Flow::ProfileDelete; + let (merchant_id, profile_id) = path.into_inner(); + + api::server_wrap( + flow, + state, + &req, + profile_id, + |state, _, profile_id, _| delete_profile(state, profile_id, &merchant_id), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + ) + .await +} +#[instrument(skip_all, fields(flow = ?Flow::ProfileList))] +pub async fn profiles_list( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::ProfileList; + let merchant_id = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + merchant_id.clone(), + |state, _auth, merchant_id, _| list_profile(state, merchant_id, None), + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromRoute { + merchant_id, + required_permission: permissions::Permission::MerchantAccountRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::ProfileList))] +pub async fn profiles_list_at_profile_level( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::ProfileList; + let merchant_id = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + merchant_id.clone(), + |state, auth, merchant_id, _| { + list_profile( + state, + merchant_id, + auth.profile_id.map(|profile_id| vec![profile_id]), + ) + }, + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromRoute { + merchant_id, + required_permission: permissions::Permission::MerchantAccountRead, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::ToggleConnectorAgnosticMit))] +pub async fn toggle_connector_agnostic_mit( + state: web::Data, + req: HttpRequest, + path: web::Path<( + common_utils::id_type::MerchantId, + common_utils::id_type::ProfileId, + )>, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ToggleConnectorAgnosticMit; + let (merchant_id, profile_id) = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _, req, _| connector_agnostic_mit_toggle(state, &merchant_id, &profile_id, req), + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: permissions::Permission::RoutingWrite, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::ToggleExtendedCardInfo))] +pub async fn toggle_extended_card_info( + state: web::Data, + req: HttpRequest, + path: web::Path<( + common_utils::id_type::MerchantId, + common_utils::id_type::ProfileId, + )>, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ToggleExtendedCardInfo; + let (merchant_id, profile_id) = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _, req, _| extended_card_info_toggle(state, &merchant_id, &profile_id, req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))] +pub async fn payment_connector_list_profile( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::MerchantConnectorsList; + let merchant_id = path.into_inner(); + + api::server_wrap( + flow, + state, + &req, + merchant_id.to_owned(), + |state, auth, merchant_id, _| { + list_payment_connectors( + state, + merchant_id, + auth.profile_id.map(|profile_id| vec![profile_id]), + ) + }, + auth::auth_type( + &auth::AdminApiAuthWithMerchantIdFromHeader, + &auth::JWTAuthMerchantFromRoute { + merchant_id, + required_permission: permissions::Permission::MerchantConnectorAccountRead, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + ) + .await +} diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index bcc2046e24e..3a56271b08a 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -497,14 +497,14 @@ impl Default for PollConfig { #[derive(Clone, Debug)] pub struct RedirectPaymentFlowResponse { pub payments_response: api_models::payments::PaymentsResponse, - pub business_profile: domain::BusinessProfile, + pub business_profile: domain::Profile, } #[derive(Clone, Debug)] pub struct AuthenticatePaymentFlowResponse { pub payments_response: api_models::payments::PaymentsResponse, pub poll_config: PollConfig, - pub business_profile: domain::BusinessProfile, + pub business_profile: domain::Profile, } #[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)] diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index e30a6ccc1fd..f50e5376db1 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -1,13 +1,15 @@ use std::collections::HashMap; +#[cfg(feature = "v2")] +pub use api_models::admin; pub use api_models::{ admin::{ - BusinessProfileCreate, BusinessProfileResponse, BusinessProfileUpdate, MerchantAccountCreate, MerchantAccountDeleteResponse, MerchantAccountResponse, MerchantAccountUpdate, MerchantConnectorCreate, MerchantConnectorDeleteResponse, MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId, MerchantConnectorResponse, MerchantDetails, MerchantId, PaymentMethodsEnabled, - ToggleAllKVRequest, ToggleAllKVResponse, ToggleKVRequest, ToggleKVResponse, WebhookDetails, + ProfileCreate, ProfileResponse, ProfileUpdate, ToggleAllKVRequest, ToggleAllKVResponse, + ToggleKVRequest, ToggleKVResponse, WebhookDetails, }, organization::{OrganizationId, OrganizationRequest, OrganizationResponse}, }; @@ -107,10 +109,10 @@ impl ForeignTryFrom for MerchantAccountResponse { } } #[cfg(feature = "v1")] -impl ForeignTryFrom for BusinessProfileResponse { +impl ForeignTryFrom for ProfileResponse { type Error = error_stack::Report; - fn foreign_try_from(item: domain::BusinessProfile) -> Result { + fn foreign_try_from(item: domain::Profile) -> Result { let profile_id = item.get_id().to_owned(); let outgoing_webhook_custom_http_headers = item .outgoing_webhook_custom_http_headers @@ -169,10 +171,10 @@ impl ForeignTryFrom for BusinessProfileResponse { } #[cfg(feature = "v2")] -impl ForeignTryFrom for BusinessProfileResponse { +impl ForeignTryFrom for admin::ProfileResponse { type Error = error_stack::Report; - fn foreign_try_from(item: domain::BusinessProfile) -> Result { + fn foreign_try_from(item: domain::Profile) -> Result { let id = item.get_id().to_owned(); let outgoing_webhook_custom_http_headers = item @@ -235,12 +237,12 @@ impl ForeignTryFrom for BusinessProfileResponse { } #[cfg(feature = "v1")] -pub async fn create_business_profile_from_merchant_account( +pub async fn create_profile_from_merchant_account( state: &SessionState, merchant_account: domain::MerchantAccount, - request: BusinessProfileCreate, + request: ProfileCreate, key_store: &MerchantKeyStore, -) -> Result> { +) -> Result> { use common_utils::ext_traits::AsyncExt; use crate::core; @@ -281,76 +283,73 @@ pub async fn create_business_profile_from_merchant_account( }) .transpose()?; - Ok(domain::BusinessProfile::from( - domain::BusinessProfileSetter { - profile_id, - merchant_id, - profile_name: request.profile_name.unwrap_or("default".to_string()), - created_at: current_time, - modified_at: current_time, - return_url: request - .return_url - .map(|return_url| return_url.to_string()) - .or(merchant_account.return_url), - enable_payment_response_hash: request - .enable_payment_response_hash - .unwrap_or(merchant_account.enable_payment_response_hash), - payment_response_hash_key: Some(payment_response_hash_key), - redirect_to_merchant_with_http_post: request - .redirect_to_merchant_with_http_post - .unwrap_or(merchant_account.redirect_to_merchant_with_http_post), - webhook_details: webhook_details.or(merchant_account.webhook_details), - metadata: request.metadata, - routing_algorithm: None, - intent_fulfillment_time: request - .intent_fulfillment_time - .map(i64::from) - .or(merchant_account.intent_fulfillment_time) - .or(Some(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME)), - frm_routing_algorithm: request - .frm_routing_algorithm - .or(merchant_account.frm_routing_algorithm), - #[cfg(feature = "payouts")] - payout_routing_algorithm: request - .payout_routing_algorithm - .or(merchant_account.payout_routing_algorithm), - #[cfg(not(feature = "payouts"))] - payout_routing_algorithm: None, - is_recon_enabled: merchant_account.is_recon_enabled, - applepay_verified_domains: request.applepay_verified_domains, - payment_link_config, - session_expiry: request - .session_expiry - .map(i64::from) - .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)), - authentication_connector_details: request - .authentication_connector_details - .map(ForeignInto::foreign_into), - payout_link_config, - is_connector_agnostic_mit_enabled: request.is_connector_agnostic_mit_enabled, - is_extended_card_info_enabled: None, - extended_card_info_config: None, - use_billing_as_payment_method_billing: request - .use_billing_as_payment_method_billing - .or(Some(true)), - collect_shipping_details_from_wallet_connector: request - .collect_shipping_details_from_wallet_connector - .or(Some(false)), - collect_billing_details_from_wallet_connector: request - .collect_billing_details_from_wallet_connector - .or(Some(false)), - always_collect_billing_details_from_wallet_connector: request - .always_collect_billing_details_from_wallet_connector - .or(Some(false)), - always_collect_shipping_details_from_wallet_connector: request - .always_collect_shipping_details_from_wallet_connector - .or(Some(false)), - outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers - .map(Into::into), - tax_connector_id: request.tax_connector_id, - is_tax_connector_enabled: request.is_tax_connector_enabled, - dynamic_routing_algorithm: None, - is_network_tokenization_enabled: request.is_network_tokenization_enabled, - }, - )) + Ok(domain::Profile::from(domain::ProfileSetter { + profile_id, + merchant_id, + profile_name: request.profile_name.unwrap_or("default".to_string()), + created_at: current_time, + modified_at: current_time, + return_url: request + .return_url + .map(|return_url| return_url.to_string()) + .or(merchant_account.return_url), + enable_payment_response_hash: request + .enable_payment_response_hash + .unwrap_or(merchant_account.enable_payment_response_hash), + payment_response_hash_key: Some(payment_response_hash_key), + redirect_to_merchant_with_http_post: request + .redirect_to_merchant_with_http_post + .unwrap_or(merchant_account.redirect_to_merchant_with_http_post), + webhook_details: webhook_details.or(merchant_account.webhook_details), + metadata: request.metadata, + routing_algorithm: None, + intent_fulfillment_time: request + .intent_fulfillment_time + .map(i64::from) + .or(merchant_account.intent_fulfillment_time) + .or(Some(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME)), + frm_routing_algorithm: request + .frm_routing_algorithm + .or(merchant_account.frm_routing_algorithm), + #[cfg(feature = "payouts")] + payout_routing_algorithm: request + .payout_routing_algorithm + .or(merchant_account.payout_routing_algorithm), + #[cfg(not(feature = "payouts"))] + payout_routing_algorithm: None, + is_recon_enabled: merchant_account.is_recon_enabled, + applepay_verified_domains: request.applepay_verified_domains, + payment_link_config, + session_expiry: request + .session_expiry + .map(i64::from) + .or(Some(common_utils::consts::DEFAULT_SESSION_EXPIRY)), + authentication_connector_details: request + .authentication_connector_details + .map(ForeignInto::foreign_into), + payout_link_config, + is_connector_agnostic_mit_enabled: request.is_connector_agnostic_mit_enabled, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + use_billing_as_payment_method_billing: request + .use_billing_as_payment_method_billing + .or(Some(true)), + collect_shipping_details_from_wallet_connector: request + .collect_shipping_details_from_wallet_connector + .or(Some(false)), + collect_billing_details_from_wallet_connector: request + .collect_billing_details_from_wallet_connector + .or(Some(false)), + always_collect_billing_details_from_wallet_connector: request + .always_collect_billing_details_from_wallet_connector + .or(Some(false)), + always_collect_shipping_details_from_wallet_connector: request + .always_collect_shipping_details_from_wallet_connector + .or(Some(false)), + outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers.map(Into::into), + tax_connector_id: request.tax_connector_id, + is_tax_connector_enabled: request.is_tax_connector_enabled, + dynamic_routing_algorithm: None, + is_network_tokenization_enabled: request.is_network_tokenization_enabled, + })) } diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index 37b8d2b8016..070e583caab 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -8,7 +8,7 @@ mod merchant_account { mod business_profile { pub use hyperswitch_domain_models::business_profile::{ - BusinessProfile, BusinessProfileGeneralUpdate, BusinessProfileSetter, BusinessProfileUpdate, + Profile, ProfileGeneralUpdate, ProfileSetter, ProfileUpdate, }; } diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index 933b8e73da8..9b8d17db42c 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -96,7 +96,7 @@ impl PaymentMethodClientSecret { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, - pub business_profile: Option, + pub business_profile: Option, pub requires_cvv: bool, pub off_session_payment_flag: bool, pub is_connector_agnostic_mit_enabled: bool, diff --git a/crates/router/src/types/storage/business_profile.rs b/crates/router/src/types/storage/business_profile.rs index 2ab7597bcda..5396bbfb2c5 100644 --- a/crates/router/src/types/storage/business_profile.rs +++ b/crates/router/src/types/storage/business_profile.rs @@ -1,3 +1 @@ -pub use diesel_models::business_profile::{ - BusinessProfile, BusinessProfileNew, BusinessProfileUpdateInternal, -}; +pub use diesel_models::business_profile::{Profile, ProfileNew, ProfileUpdateInternal}; diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index b603b86a72c..edeb3f12fa7 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -1077,7 +1077,7 @@ pub fn check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata( #[allow(clippy::too_many_arguments)] pub async fn trigger_payments_webhook( merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: D, customer: Option, @@ -1096,7 +1096,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn trigger_payments_webhook( merchant_account: domain::MerchantAccount, - business_profile: domain::BusinessProfile, + business_profile: domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: D, customer: Option, diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index f030df01fc0..1f7d75fba83 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -90,7 +90,7 @@ pub async fn generate_sample_data( state .store - .list_business_profile_by_merchant_id(key_manager_state, &key_store, merchant_id) + .list_profile_by_merchant_id(key_manager_state, &key_store, merchant_id) .await .change_context(SampleDataError::InternalServerError) .attach_printable("Failed to get business profile")? diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index 3936ff60b72..b4008bd0008 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -382,7 +382,7 @@ pub async fn get_single_merchant_id_and_profile_id( state .store - .list_business_profile_by_merchant_id(&state.into(), &key_store, &merchant_id) + .list_profile_by_merchant_id(&state.into(), &key_store, &merchant_id) .await .change_context(UserErrors::InternalServerError)? .pop() diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index 3701418c8f1..f34d707b6c8 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -190,11 +190,9 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { profile_id, ) .await - .to_not_found_response( - errors::ApiErrorResponse::BusinessProfileNotFound { - id: profile_id.get_string_repr().to_owned(), - }, - )?; + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { + id: profile_id.get_string_repr().to_owned(), + })?; // Trigger the outgoing webhook to notify the merchant about failed payment let operation = operations::PaymentStatus; diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 310f5ccecbf..12e30f54549 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -306,16 +306,16 @@ pub enum Flow { PaymentLinkList, /// Payment Link Status PaymentLinkStatus, - /// Create a business profile - BusinessProfileCreate, - /// Update a business profile - BusinessProfileUpdate, - /// Retrieve a business profile - BusinessProfileRetrieve, - /// Delete a business profile - BusinessProfileDelete, - /// List all the business profiles for a merchant - BusinessProfileList, + /// Create a profile + ProfileCreate, + /// Update a profile + ProfileUpdate, + /// Retrieve a profile + ProfileRetrieve, + /// Delete a profile + ProfileDelete, + /// List all the profiles for a merchant + ProfileList, /// Different verification flows Verification, /// Rust locker migration diff --git a/crates/storage_impl/src/mock_db.rs b/crates/storage_impl/src/mock_db.rs index 3434bcb67dd..b3358d898b2 100644 --- a/crates/storage_impl/src/mock_db.rs +++ b/crates/storage_impl/src/mock_db.rs @@ -43,7 +43,7 @@ pub struct MockDb { pub mandates: Arc>>, pub captures: Arc>>, pub merchant_key_store: Arc>>, - pub business_profiles: Arc>>, + pub business_profiles: Arc>>, pub reverse_lookups: Arc>>, pub payment_link: Arc>>, pub organizations: Arc>>, From 90949d94cf3cdfba7ae4deb7d931b0a40e330310 Mon Sep 17 00:00:00 2001 From: Pa1NarK <69745008+pixincreate@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:30:41 +0530 Subject: [PATCH 33/48] fix(cypress): fix user login in routing cypress framework (#5950) --- .../cypress/e2e/PaymentUtils/Commons.js | 14 ++- .../cypress/e2e/PaymentUtils/Novalnet.js | 48 ++++---- .../cypress/e2e/PayoutUtils/Commons.js | 15 +++ .../RoutingTest/00000-PriorityRouting.cy.js | 14 +-- .../00001-VolumeBasedRouting.cy.js | 15 +-- .../RoutingTest/00002-RuleBasedRouting.cy.js | 13 +-- .../e2e/RoutingTest/00003-Retries.cy.js | 10 +- cypress-tests/cypress/support/commands.js | 106 +++++++++++++----- 8 files changed, 152 insertions(+), 83 deletions(-) diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Commons.js b/cypress-tests/cypress/e2e/PaymentUtils/Commons.js index eff36cf502c..284eff5fd1d 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Commons.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Commons.js @@ -22,8 +22,18 @@ function normalise(input) { }; if (typeof input !== "string") { - const spec_name = Cypress.spec.name.split("-")[1].split(".")[0]; - return `${spec_name}`; + const specName = Cypress.spec.name; + + if (specName.includes("-")) { + const parts = specName.split("-"); + + if (parts.length > 1 && parts[1].includes(".")) { + return parts[1].split(".")[0]; + } + } + + // Fallback + return `${specName}`; } const lowerCaseInput = input.toLowerCase(); diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Novalnet.js b/cypress-tests/cypress/e2e/PaymentUtils/Novalnet.js index 6fe7dc63922..e0db22b347d 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Novalnet.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Novalnet.js @@ -34,21 +34,21 @@ export const connectorDetails = { payment_method: "card", billing: { address: { - line1: "1467", - line2: "CA", - line3: "CA", - city: "Musterhausen", - state: "California", - zip: "12345", - country: "DE", - first_name: "Max", - last_name: "Mustermann" + line1: "1467", + line2: "CA", + line3: "CA", + city: "Musterhausen", + state: "California", + zip: "12345", + country: "DE", + first_name: "Max", + last_name: "Mustermann", }, email: "test@novalnet.de", phone: { - number: "9123456789", - country_code: "+91" - } + number: "9123456789", + country_code: "+91", + }, }, payment_method_data: { card: successfulThreeDSTestCardDetails, @@ -68,21 +68,21 @@ export const connectorDetails = { payment_method: "card", billing: { address: { - line1: "1467", - line2: "CA", - line3: "CA", - city: "Musterhausen", - state: "California", - zip: "12345", - country: "DE", - first_name: "Max", - last_name: "Mustermann" + line1: "1467", + line2: "CA", + line3: "CA", + city: "Musterhausen", + state: "California", + zip: "12345", + country: "DE", + first_name: "Max", + last_name: "Mustermann", }, email: "test@novalnet.de", phone: { - number: "9123456789", - country_code: "+91" - } + number: "9123456789", + country_code: "+91", + }, }, payment_method_data: { card: successfulThreeDSTestCardDetails, diff --git a/cypress-tests/cypress/e2e/PayoutUtils/Commons.js b/cypress-tests/cypress/e2e/PayoutUtils/Commons.js index 248d0fe2a4d..5f8a580731e 100644 --- a/cypress-tests/cypress/e2e/PayoutUtils/Commons.js +++ b/cypress-tests/cypress/e2e/PayoutUtils/Commons.js @@ -18,6 +18,21 @@ function normalise(input) { // Add more known exceptions here }; + if (typeof input !== "string") { + const specName = Cypress.spec.name; + + if (specName.includes("-")) { + const parts = specName.split("-"); + + if (parts.length > 1 && parts[1].includes(".")) { + return parts[1].split(".")[0]; + } + } + + // Fallback + return `${specName}`; + } + if (exceptions[input.toLowerCase()]) { return exceptions[input.toLowerCase()]; } else { diff --git a/cypress-tests/cypress/e2e/RoutingTest/00000-PriorityRouting.cy.js b/cypress-tests/cypress/e2e/RoutingTest/00000-PriorityRouting.cy.js index 09f237d7d47..3037a6eb99e 100644 --- a/cypress-tests/cypress/e2e/RoutingTest/00000-PriorityRouting.cy.js +++ b/cypress-tests/cypress/e2e/RoutingTest/00000-PriorityRouting.cy.js @@ -6,7 +6,8 @@ let globalState; describe("Priority Based Routing Test", () => { let should_continue = true; - context("Create Jwt Token", () => { + + context("Login", () => { before("seed global state", () => { cy.task("getGlobalState").then((state) => { globalState = new State(state); @@ -16,14 +17,11 @@ describe("Priority Based Routing Test", () => { after("flush global state", () => { cy.task("setGlobalState", globalState.data); }); - it("create-jwt-token", () => { - let data = utils.getConnectorDetails("common")["jwt"]; - let req_data = data["Request"]; - let res_data = data["Response"]; - cy.createJWTToken(req_data, res_data, globalState); - if (should_continue) - should_continue = utils.should_continue_further(res_data); + it("User login", () => { + cy.userLogin(globalState); + cy.terminate2Fa(globalState); + cy.userInfo(globalState); }); it("merchant retrieve call", () => { diff --git a/cypress-tests/cypress/e2e/RoutingTest/00001-VolumeBasedRouting.cy.js b/cypress-tests/cypress/e2e/RoutingTest/00001-VolumeBasedRouting.cy.js index e0b1ee589de..665afc97581 100644 --- a/cypress-tests/cypress/e2e/RoutingTest/00001-VolumeBasedRouting.cy.js +++ b/cypress-tests/cypress/e2e/RoutingTest/00001-VolumeBasedRouting.cy.js @@ -5,9 +5,7 @@ import * as utils from "../RoutingUtils/Utils"; let globalState; describe("Volume Based Routing Test", () => { - let should_continue = true; - - context("Create Jwt Token", () => { + context("Login", () => { before("seed global state", () => { cy.task("getGlobalState").then((state) => { globalState = new State(state); @@ -17,14 +15,11 @@ describe("Volume Based Routing Test", () => { after("flush global state", () => { cy.task("setGlobalState", globalState.data); }); - it("create-jwt-token", () => { - let data = utils.getConnectorDetails("common")["jwt"]; - let req_data = data["Request"]; - let res_data = data["Response"]; - cy.createJWTToken(req_data, res_data, globalState); - if (should_continue) - should_continue = utils.should_continue_further(res_data); + it("User login", () => { + cy.userLogin(globalState); + cy.terminate2Fa(globalState); + cy.userInfo(globalState); }); it("merchant retrieve call", () => { diff --git a/cypress-tests/cypress/e2e/RoutingTest/00002-RuleBasedRouting.cy.js b/cypress-tests/cypress/e2e/RoutingTest/00002-RuleBasedRouting.cy.js index 583573ba699..c98f34a55bb 100644 --- a/cypress-tests/cypress/e2e/RoutingTest/00002-RuleBasedRouting.cy.js +++ b/cypress-tests/cypress/e2e/RoutingTest/00002-RuleBasedRouting.cy.js @@ -5,9 +5,7 @@ import * as utils from "../RoutingUtils/Utils"; let globalState; describe("Rule Based Routing Test", () => { - let should_continue = true; - - context("Create Jwt Token", () => { + context("Login", () => { before("seed global state", () => { cy.task("getGlobalState").then((state) => { globalState = new State(state); @@ -17,12 +15,11 @@ describe("Rule Based Routing Test", () => { after("flush global state", () => { cy.task("setGlobalState", globalState.data); }); - it("create-jwt-token", () => { - let data = utils.getConnectorDetails("common")["jwt"]; - let req_data = data["Request"]; - let res_data = data["Response"]; - cy.createJWTToken(req_data, res_data, globalState); + it("User login", () => { + cy.userLogin(globalState); + cy.terminate2Fa(globalState); + cy.userInfo(globalState); }); it("merchant retrieve call", () => { diff --git a/cypress-tests/cypress/e2e/RoutingTest/00003-Retries.cy.js b/cypress-tests/cypress/e2e/RoutingTest/00003-Retries.cy.js index 86758215001..2638b18f479 100644 --- a/cypress-tests/cypress/e2e/RoutingTest/00003-Retries.cy.js +++ b/cypress-tests/cypress/e2e/RoutingTest/00003-Retries.cy.js @@ -16,12 +16,10 @@ describe("Auto Retries & Step Up 3DS", () => { cy.task("setGlobalState", globalState.data); }); - it("Create JWT token", () => { - let data = utils.getConnectorDetails("common")["jwt"]; - let req_data = data["Request"]; - let res_data = data["Response"]; - - cy.createJWTToken(req_data, res_data, globalState); + it("User login", () => { + cy.userLogin(globalState); + cy.terminate2Fa(globalState); + cy.userInfo(globalState); }); it("List MCA", () => { diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index 3c7cbad2f9e..396be8e59f4 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -967,7 +967,6 @@ Cypress.Commands.add( (confirmBody, req_data, res_data, confirm, globalState) => { const paymentIntentId = globalState.get("paymentID"); const connectorId = globalState.get("connectorId"); - console.log("connectorId", connectorId); for (const key in req_data) { confirmBody[key] = req_data[key]; } @@ -989,7 +988,6 @@ Cypress.Commands.add( expect(response.headers["content-type"]).to.include("application/json"); globalState.set("paymentID", paymentIntentId); globalState.set("connectorId", response.body.connector); - console.log("connectorId", response.body.connector); globalState.set("paymentMethodType", confirmBody.payment_method_type); switch (response.body.authentication_type) { @@ -2063,46 +2061,104 @@ Cypress.Commands.add("retrievePayoutCallTest", (globalState) => { }); }); -Cypress.Commands.add("createJWTToken", (req_data, res_data, globalState) => { - const jwt_body = { +// User API calls +// Below 3 commands should be called in sequence to login a user +Cypress.Commands.add("userLogin", (globalState) => { + // Define the necessary variables and constant + const base_url = globalState.get("baseUrl"); + const query_params = `token_only=true`; + const signin_body = { email: `${globalState.get("email")}`, password: `${globalState.get("password")}`, }; + const url = `${base_url}/user/v2/signin?${query_params}`; cy.request({ method: "POST", - url: `${globalState.get("baseUrl")}/user/v2/signin`, + url: url, headers: { "Content-Type": "application/json", }, + body: signin_body, failOnStatusCode: false, - body: jwt_body, }).then((response) => { logRequestId(response.headers["x-request-id"]); - expect(response.headers["content-type"]).to.include("application/json"); if (response.status === 200) { - expect(response.body).to.have.property("token"); - //set jwt_token - globalState.set("jwtToken", response.body.token); + if (response.body.token_type === "totp") { + expect(response.body).to.have.property("token").and.to.not.be.empty; - //setting merchantId for manual create merchant - globalState.set("merchantId", response.body.merchant_id); + globalState.set("totpToken", response.body.token); + cy.task("setGlobalState", globalState.data); + } + } else { + throw new Error( + `User login call failed to get totp token with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("terminate2Fa", (globalState) => { + // Define the necessary variables and constant + const base_url = globalState.get("baseUrl"); + const query_params = `skip_two_factor_auth=true`; + const api_key = globalState.get("totpToken"); + const url = `${base_url}/user/2fa/terminate?${query_params}`; - // set session cookie - const sessionCookie = response.headers["set-cookie"][0]; - const sessionValue = sessionCookie.split(";")[0]; - globalState.set("cookie", sessionValue); + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); - // set api key - globalState.set("apiKey", globalState.get("routingApiKey")); - globalState.set("merchantId", response.body.merchant_id); + if (response.status === 200) { + if (response.body.token_type === "user_info") { + expect(response.body).to.have.property("token").and.to.not.be.empty; - for (const key in res_data.body) { - expect(res_data.body[key]).to.equal(response.body[key]); + globalState.set("userInfoToken", response.body.token); + cy.task("setGlobalState", globalState.data); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `2FA terminate call failed with status ${response.status} and message ${response.body.message}` + ); + } + }); +}); +Cypress.Commands.add("userInfo", (globalState) => { + // Define the necessary variables and constant + const base_url = globalState.get("baseUrl"); + const api_key = globalState.get("userInfoToken"); + const url = `${base_url}/user`; + + cy.request({ + method: "GET", + url: url, + headers: { + Authorization: `Bearer ${api_key}`, + "Content-Type": "application/json", + }, + failOnStatusCode: false, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + + if (response.status === 200) { + expect(response.body).to.have.property("merchant_id").and.to.not.be.empty; + expect(response.body).to.have.property("org_id").and.to.not.be.empty; + expect(response.body).to.have.property("profile_id").and.to.not.be.empty; + + globalState.set("merchantId", response.body.merchant_id); + globalState.set("organizationId", response.body.org_id); + globalState.set("profileId", response.body.profile_id); + } else { + throw new Error( + `User login call failed to fetch user info with status ${response.status} and message ${response.body.message}` + ); } }); }); @@ -2145,9 +2201,9 @@ Cypress.Commands.add( method: "POST", url: `${globalState.get("baseUrl")}/routing`, headers: { + Authorization: `Bearer ${globalState.get("userInfoToken")}`, "Content-Type": "application/json", Cookie: `${globalState.get("cookie")}`, - "api-key": `Bearer ${globalState.get("jwtToken")}`, }, failOnStatusCode: false, body: routingBody, @@ -2176,9 +2232,9 @@ Cypress.Commands.add( method: "POST", url: `${globalState.get("baseUrl")}/routing/${routing_config_id}/activate`, headers: { + Authorization: `Bearer ${globalState.get("userInfoToken")}`, "Content-Type": "application/json", Cookie: `${globalState.get("cookie")}`, - "api-key": globalState.get("apiKey"), }, failOnStatusCode: false, }).then((response) => { @@ -2205,9 +2261,9 @@ Cypress.Commands.add( method: "GET", url: `${globalState.get("baseUrl")}/routing/${routing_config_id}`, headers: { + Authorization: `Bearer ${globalState.get("userInfoToken")}`, "Content-Type": "application/json", Cookie: `${globalState.get("cookie")}`, - "api-key": globalState.get("apiKey"), }, failOnStatusCode: false, }).then((response) => { From 1151d215a620b1a58e48adbef6b3a39196bc91e1 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Fri, 20 Sep 2024 01:45:20 +0530 Subject: [PATCH 34/48] refactor(router): add `phone` and `country_code` in dynamic fields (#5968) --- crates/router/src/configs/defaults.rs | 36 +++++++++++++++++++ .../router/src/core/payment_methods/cards.rs | 6 +++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index 4fcb0785ea1..41efc6e4c38 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -12335,6 +12335,24 @@ pub fn get_billing_required_fields() -> HashMap { value: None, }, ), + ( + "billing.phone.number".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.phone.number".to_string(), + display_name: "phone_number".to_string(), + field_type: enums::FieldType::UserPhoneNumber, + value: None, + }, + ), + ( + "billing.phone.country_code".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.phone.country_code".to_string(), + display_name: "dialing_code".to_string(), + field_type: enums::FieldType::UserPhoneNumberCountryCode, + value: None, + }, + ), ]) } @@ -12405,6 +12423,24 @@ pub fn get_shipping_required_fields() -> HashMap { value: None, }, ), + ( + "shipping.phone.number".to_string(), + RequiredFieldInfo { + required_field: "shipping.phone.number".to_string(), + display_name: "phone_number".to_string(), + field_type: enums::FieldType::UserPhoneNumber, + value: None, + }, + ), + ( + "shipping.phone.country_code".to_string(), + RequiredFieldInfo { + required_field: "shipping.phone.country_code".to_string(), + display_name: "dialing_code".to_string(), + field_type: enums::FieldType::UserPhoneNumberCountryCode, + value: None, + }, + ), ]) } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index d8ad8dbead4..efc9621d26d 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -3840,7 +3840,11 @@ fn should_collect_shipping_or_billing_details_from_wallet_connector( mut required_fields_hs: HashMap, ) -> HashMap { match (payment_method, payment_experience_optional) { - (api_enums::PaymentMethod::Wallet, Some(api_enums::PaymentExperience::InvokeSdkClient)) => { + (api_enums::PaymentMethod::Wallet, Some(api_enums::PaymentExperience::InvokeSdkClient)) + | ( + api_enums::PaymentMethod::PayLater, + Some(api_enums::PaymentExperience::InvokeSdkClient), + ) => { let always_send_billing_details = business_profile.and_then(|business_profile| { business_profile.always_collect_billing_details_from_wallet_connector }); From a94cf25bb6eb40fafc5327aceffac8292b47b001 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 00:24:40 +0000 Subject: [PATCH 35/48] chore(version): 2024.09.20.0 --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5c8a29d5c8..d43b35d96bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,36 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.09.20.0 + +### Features + +- **connector:** [Novalnet] add Recurring payment flow for cards ([#5921](https://github.com/juspay/hyperswitch/pull/5921)) ([`6a6ce17`](https://github.com/juspay/hyperswitch/commit/6a6ce17506932e0843140ef5b02ed201d0524d5d)) +- **cypress-v2:** Add cypress v2 core flow tests ([#5791](https://github.com/juspay/hyperswitch/pull/5791)) ([`82574c0`](https://github.com/juspay/hyperswitch/commit/82574c0e8e7eb69e9f21eedc765145c724960cd5)) +- **payments:** Store and propagate additional wallet pm details in payments response ([#5869](https://github.com/juspay/hyperswitch/pull/5869)) ([`8320dc0`](https://github.com/juspay/hyperswitch/commit/8320dc07fe1b8b1c9427f70dcb9d952eef01a63b)) +- **payout:** Add unified error code and messages along with translation ([#5810](https://github.com/juspay/hyperswitch/pull/5810)) ([`a0f4bb7`](https://github.com/juspay/hyperswitch/commit/a0f4bb771b583a8dad2a58158c64b7a8baff24d5)) + +### Bug Fixes + +- **cypress:** Fix user login in routing cypress framework ([#5950](https://github.com/juspay/hyperswitch/pull/5950)) ([`90949d9`](https://github.com/juspay/hyperswitch/commit/90949d94cf3cdfba7ae4deb7d931b0a40e330310)) +- **payments:** Add time range in list payment attempts query ([#5959](https://github.com/juspay/hyperswitch/pull/5959)) ([`156a161`](https://github.com/juspay/hyperswitch/commit/156a161f3eb0553dcfcc1f361e42ec7713f0fbb4)) + +### Refactors + +- **dynamic_fields:** Populate `billing.email` with customer email if not present ([#5962](https://github.com/juspay/hyperswitch/pull/5962)) ([`f4fa4cd`](https://github.com/juspay/hyperswitch/commit/f4fa4cdab478d33b6e9528c3649197bc5331cecd)) +- **email:** Add recipient emails in email config ([#5964](https://github.com/juspay/hyperswitch/pull/5964)) ([`5942e05`](https://github.com/juspay/hyperswitch/commit/5942e059e9efa3fa71a13cacc896509515e2f976)) +- **recon:** Use AuthDataWithUser and use JWTAuth for token verif… ([#5829](https://github.com/juspay/hyperswitch/pull/5829)) ([`30dd7ce`](https://github.com/juspay/hyperswitch/commit/30dd7ceb5f38849faacee5409112a8857df71972)) +- **router:** Add `phone` and `country_code` in dynamic fields ([#5968](https://github.com/juspay/hyperswitch/pull/5968)) ([`1151d21`](https://github.com/juspay/hyperswitch/commit/1151d215a620b1a58e48adbef6b3a39196bc91e1)) +- Rename business profile to profiles in api, diesel, domain, interface and error types ([#5877](https://github.com/juspay/hyperswitch/pull/5877)) ([`dee91b3`](https://github.com/juspay/hyperswitch/commit/dee91b366aacd2a75ca69ae95f2eea9ef4749d20)) + +### Miscellaneous Tasks + +- Remove network tokenization service config in docker compose config ([#5953](https://github.com/juspay/hyperswitch/pull/5953)) ([`c7f7d3c`](https://github.com/juspay/hyperswitch/commit/c7f7d3c16abfec84b7ec36b1b5ea75823b40a983)) + +**Full Changelog:** [`2024.09.19.1...2024.09.20.0`](https://github.com/juspay/hyperswitch/compare/2024.09.19.1...2024.09.20.0) + +- - - + ## 2024.09.19.1 ### Refactors From c8f7232a3001be1fc5d8b0fedfd703030df83789 Mon Sep 17 00:00:00 2001 From: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:09:38 +0530 Subject: [PATCH 36/48] fix: do not allow duplicate organization name (#5919) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 33 +++++++++++++++---- api-reference/openapi_spec.json | 33 +++++++++++++++---- crates/api_models/src/events.rs | 7 ++-- crates/api_models/src/organization.rs | 13 +++++++- crates/openapi/src/openapi.rs | 3 +- crates/openapi/src/openapi_v2.rs | 3 +- crates/openapi/src/routes/organization.rs | 8 ++--- crates/router/src/core/admin.rs | 8 +++-- crates/router/src/routes/admin.rs | 4 +-- crates/router/src/types/api/admin.rs | 4 ++- crates/router/src/types/transformers.rs | 8 ++--- .../down.sql | 2 ++ .../up.sql | 3 ++ 13 files changed, 98 insertions(+), 31 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 6ff3e0176e9..926af8c162c 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -32,7 +32,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationRequest" + "$ref": "#/components/schemas/OrganizationCreateRequest" }, "examples": { "Create an organization with organization_name": { @@ -129,7 +129,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationRequest" + "$ref": "#/components/schemas/OrganizationUpdateRequest" }, "examples": { "Update organization_name of the organization": { @@ -10116,12 +10116,14 @@ "confirm" ] }, - "OrganizationRequest": { + "OrganizationCreateRequest": { "type": "object", + "required": [ + "organization_name" + ], "properties": { "organization_name": { - "type": "string", - "nullable": true + "type": "string" }, "organization_details": { "type": "object", @@ -10131,7 +10133,8 @@ "type": "object", "nullable": true } - } + }, + "additionalProperties": false }, "OrganizationResponse": { "type": "object", @@ -10169,6 +10172,24 @@ } } }, + "OrganizationUpdateRequest": { + "type": "object", + "properties": { + "organization_name": { + "type": "string", + "nullable": true + }, + "organization_details": { + "type": "object", + "nullable": true + }, + "metadata": { + "type": "object", + "nullable": true + } + }, + "additionalProperties": false + }, "OutgoingWebhook": { "type": "object", "required": [ diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 4934b4fb345..2635cdb42fe 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -1121,7 +1121,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationRequest" + "$ref": "#/components/schemas/OrganizationCreateRequest" }, "examples": { "Create an organization with organization_name": { @@ -1218,7 +1218,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationRequest" + "$ref": "#/components/schemas/OrganizationUpdateRequest" }, "examples": { "Update organization_name of the organization": { @@ -13913,12 +13913,14 @@ } } }, - "OrganizationRequest": { + "OrganizationCreateRequest": { "type": "object", + "required": [ + "organization_name" + ], "properties": { "organization_name": { - "type": "string", - "nullable": true + "type": "string" }, "organization_details": { "type": "object", @@ -13928,7 +13930,8 @@ "type": "object", "nullable": true } - } + }, + "additionalProperties": false }, "OrganizationResponse": { "type": "object", @@ -13966,6 +13969,24 @@ } } }, + "OrganizationUpdateRequest": { + "type": "object", + "properties": { + "organization_name": { + "type": "string", + "nullable": true + }, + "organization_details": { + "type": "object", + "nullable": true + }, + "metadata": { + "type": "object", + "nullable": true + } + }, + "additionalProperties": false + }, "OutgoingWebhook": { "type": "object", "required": [ diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index b10f8e32dba..29fe0bed220 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -32,7 +32,9 @@ use crate::{ disputes::*, files::*, mandates::*, - organization::{OrganizationId, OrganizationRequest, OrganizationResponse}, + organization::{ + OrganizationCreateRequest, OrganizationId, OrganizationResponse, OrganizationUpdateRequest, + }, payment_methods::*, payments::*, user::{UserKeyTransferRequest, UserTransferKeyResponse}, @@ -129,7 +131,8 @@ impl_api_event_type!( DisputeFiltersResponse, GetDisputeMetricRequest, OrganizationResponse, - OrganizationRequest, + OrganizationCreateRequest, + OrganizationUpdateRequest, OrganizationId, CustomerListRequest ) diff --git a/crates/api_models/src/organization.rs b/crates/api_models/src/organization.rs index 1e2203eea3b..f95a1595116 100644 --- a/crates/api_models/src/organization.rs +++ b/crates/api_models/src/organization.rs @@ -20,7 +20,18 @@ pub struct OrganizationId { } #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] -pub struct OrganizationRequest { +#[serde(deny_unknown_fields)] +pub struct OrganizationCreateRequest { + pub organization_name: String, + #[schema(value_type = Option)] + pub organization_details: Option, + #[schema(value_type = Option)] + pub metadata: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] +#[serde(deny_unknown_fields)] +pub struct OrganizationUpdateRequest { pub organization_name: Option, #[schema(value_type = Option)] pub organization_details: Option, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 0c9010421ba..f4cceec8bec 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -202,7 +202,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::refunds::RefundResponse, api_models::refunds::RefundStatus, api_models::refunds::RefundUpdateRequest, - api_models::organization::OrganizationRequest, + api_models::organization::OrganizationCreateRequest, + api_models::organization::OrganizationUpdateRequest, api_models::organization::OrganizationResponse, api_models::admin::MerchantAccountCreate, api_models::admin::MerchantAccountUpdate, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index ca2974d011a..da0a95aa6de 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -127,7 +127,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::refunds::RefundResponse, api_models::refunds::RefundStatus, api_models::refunds::RefundUpdateRequest, - api_models::organization::OrganizationRequest, + api_models::organization::OrganizationCreateRequest, + api_models::organization::OrganizationUpdateRequest, api_models::organization::OrganizationResponse, api_models::admin::MerchantAccountCreate, api_models::admin::MerchantAccountUpdate, diff --git a/crates/openapi/src/routes/organization.rs b/crates/openapi/src/routes/organization.rs index 44d5f943574..ad15c0e679a 100644 --- a/crates/openapi/src/routes/organization.rs +++ b/crates/openapi/src/routes/organization.rs @@ -6,7 +6,7 @@ post, path = "/organization", request_body( - content = OrganizationRequest, + content = OrganizationCreateRequest, examples( ( "Create an organization with organization_name" = ( @@ -51,7 +51,7 @@ pub async fn organization_retrieve() {} put, path = "/organization/{organization_id}", request_body( - content = OrganizationRequest, + content = OrganizationUpdateRequest, examples( ( "Update organization_name of the organization" = ( @@ -79,7 +79,7 @@ pub async fn organization_update() {} post, path = "/v2/organization", request_body( - content = OrganizationRequest, + content = OrganizationCreateRequest, examples( ( "Create an organization with organization_name" = ( @@ -124,7 +124,7 @@ pub async fn organization_retrieve() {} put, path = "/v2/organization/{organization_id}", request_body( - content = OrganizationRequest, + content = OrganizationUpdateRequest, examples( ( "Update organization_name of the organization" = ( diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 4000add34eb..65bdeb04a4a 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -114,14 +114,16 @@ fn add_publishable_key_to_decision_service( #[cfg(feature = "olap")] pub async fn create_organization( state: SessionState, - req: api::OrganizationRequest, + req: api::OrganizationCreateRequest, ) -> RouterResponse { let db_organization = ForeignFrom::foreign_from(req); state .store .insert_organization(db_organization) .await - .to_duplicate_response(errors::ApiErrorResponse::InternalServerError) + .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { + message: "Organization with the given organization_name already exists".to_string(), + }) .attach_printable("Error when creating organization") .map(ForeignFrom::foreign_from) .map(service_api::ApplicationResponse::Json) @@ -131,7 +133,7 @@ pub async fn create_organization( pub async fn update_organization( state: SessionState, org_id: api::OrganizationId, - req: api::OrganizationRequest, + req: api::OrganizationUpdateRequest, ) -> RouterResponse { let organization_update = diesel_models::organization::OrganizationUpdate::Update { organization_name: req.organization_name, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 14711bef6a0..e5a18fba536 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -14,7 +14,7 @@ use crate::{ pub async fn organization_create( state: web::Data, req: HttpRequest, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::OrganizationCreate; Box::pin(api::server_wrap( @@ -35,7 +35,7 @@ pub async fn organization_update( state: web::Data, req: HttpRequest, org_id: web::Path, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::OrganizationUpdate; let organization_id = org_id.into_inner(); diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index f50e5376db1..cb75fe6a9b0 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -11,7 +11,9 @@ pub use api_models::{ ProfileCreate, ProfileResponse, ProfileUpdate, ToggleAllKVRequest, ToggleAllKVResponse, ToggleKVRequest, ToggleKVResponse, WebhookDetails, }, - organization::{OrganizationId, OrganizationRequest, OrganizationResponse}, + organization::{ + OrganizationCreateRequest, OrganizationId, OrganizationResponse, OrganizationUpdateRequest, + }, }; use common_utils::ext_traits::ValueExt; use diesel_models::organization::OrganizationBridge; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 4ecda43f7e8..e9a70cda9c5 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1561,17 +1561,17 @@ impl ForeignFrom } } -impl ForeignFrom +impl ForeignFrom for diesel_models::organization::OrganizationNew { - fn foreign_from(item: api_models::organization::OrganizationRequest) -> Self { + fn foreign_from(item: api_models::organization::OrganizationCreateRequest) -> Self { let org_new = api_models::organization::OrganizationNew::new(None); - let api_models::organization::OrganizationRequest { + let api_models::organization::OrganizationCreateRequest { organization_name, organization_details, metadata, } = item; - let mut org_new_db = Self::new(org_new.org_id, organization_name); + let mut org_new_db = Self::new(org_new.org_id, Some(organization_name)); org_new_db.organization_details = organization_details; org_new_db.metadata = metadata; org_new_db diff --git a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql index 99af8b6deda..6acb08597f5 100644 --- a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql +++ b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql @@ -8,6 +8,8 @@ ALTER TABLE ORGANIZATION DROP CONSTRAINT organization_pkey_id; ALTER TABLE ORGANIZATION ADD CONSTRAINT organization_pkey PRIMARY KEY (org_id); +ALTER TABLE organization DROP CONSTRAINT organization_organization_name_key; + -- back fill UPDATE ORGANIZATION SET org_name = organization_name diff --git a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql index cde27046bcf..440391529ab 100644 --- a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql +++ b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql @@ -16,6 +16,9 @@ ALTER TABLE ORGANIZATION DROP CONSTRAINT organization_pkey; ALTER TABLE ORGANIZATION ADD CONSTRAINT organization_pkey_id PRIMARY KEY (id); +ALTER TABLE ORGANIZATION +ADD CONSTRAINT organization_organization_name_key UNIQUE (organization_name); + ------------------------ Merchant Account ----------------------- -- The new primary key for v2 merchant account will be `id` ALTER TABLE merchant_account DROP CONSTRAINT merchant_account_pkey; From dccb8d4e629d615a69f6922a96e72f10d3410bfc Mon Sep 17 00:00:00 2001 From: Gnanasundari24 <118818938+Gnanasundari24@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:45:13 +0530 Subject: [PATCH 37/48] ci(test): Fix nmi postman collection (#5928) Co-authored-by: Sanchith Hegde --- .../Scenario10-Add card flow/.meta.json | 1 + .../Payments - Retrieve/event.test.js | 14 ++-- .../Payments - Retrieve/request.json | 15 ++-- .../.meta.json | 4 +- .../Payments - Create/event.test.js | 71 ++++++++++++------- .../Payments - Retrieve/.event.meta.json | 5 ++ .../Payments - Retrieve/event.test.js | 61 ++++++++++++++++ .../Payments - Retrieve/request.json | 28 ++++++++ .../Payments - Retrieve/response.json | 1 + 9 files changed, 160 insertions(+), 40 deletions(-) create mode 100644 postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/.event.meta.json create mode 100644 postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/event.test.js create mode 100644 postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/request.json create mode 100644 postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/response.json diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/.meta.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/.meta.json index 5f65896adb0..7d5a8078af1 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/.meta.json +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/.meta.json @@ -1,6 +1,7 @@ { "childrenOrder": [ "Payments - Create", + "Payments - Retrieve", "List payment methods for a Customer", "Save card payments - Create", "Save card payments - Confirm", diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/event.test.js b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/event.test.js index d0a02af7436..814cc4496dd 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/event.test.js +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/event.test.js @@ -6,7 +6,7 @@ pm.test("[GET]::/payments/:id - Status code is 2xx", function () { // Validate if response header has matching content-type pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include( - "application/json", + "application/json" ); }); @@ -26,11 +26,11 @@ if (jsonData?.payment_id) { pm.collectionVariables.set("payment_id", jsonData.payment_id); console.log( "- use {{payment_id}} as collection variable for value", - jsonData.payment_id, + jsonData.payment_id ); } else { console.log( - "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." ); } @@ -39,11 +39,11 @@ if (jsonData?.mandate_id) { pm.collectionVariables.set("mandate_id", jsonData.mandate_id); console.log( "- use {{mandate_id}} as collection variable for value", - jsonData.mandate_id, + jsonData.mandate_id ); } else { console.log( - "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." ); } @@ -52,10 +52,10 @@ if (jsonData?.client_secret) { pm.collectionVariables.set("client_secret", jsonData.client_secret); console.log( "- use {{client_secret}} as collection variable for value", - jsonData.client_secret, + jsonData.client_secret ); } else { console.log( - "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." ); } diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/request.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/request.json index c71774083b2..6cd4b7d96c5 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/request.json +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Retrieve/request.json @@ -7,13 +7,14 @@ } ], "url": { - "raw": "{{baseUrl}}/payments/:id", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments", - ":id" + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": ["{{baseUrl}}"], + "path": ["payments", ":id"], + "query": [ + { + "key": "force_sync", + "value": "true" + } ], "variable": [ { diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/.meta.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/.meta.json index 1ebea8f5e86..9fdf39812c1 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/.meta.json +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/.meta.json @@ -1,8 +1,8 @@ { "childrenOrder": [ "Payments - Create", + "Payments - Retrieve", "List payment methods for a Customer", "Save card payments - Create", "Save card payments - Confirm" - ] -} + ]} diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/event.test.js b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/event.test.js index cd3e43df900..c2bed81a7b1 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/event.test.js +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/event.test.js @@ -1,46 +1,64 @@ -// Validate status 2xx +// Validate status 2xx pm.test("[POST]::/payments - Status code is 2xx", function () { - pm.response.to.be.success; + pm.response.to.be.success; }); // Validate if response header has matching content-type pm.test("[POST]::/payments - Content-Type is application/json", function () { - pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json"); + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json" + ); }); -// Validate if response has JSON Body +// Validate if response has JSON Body pm.test("[POST]::/payments - Response has JSON Body", function () { - pm.response.to.have.jsonBody(); + pm.response.to.have.jsonBody(); }); // Set response object as internal variable let jsonData = {}; -try {jsonData = pm.response.json();}catch(e){} +try { + jsonData = pm.response.json(); +} catch (e) {} // pm.collectionVariables - Set payment_id as variable for jsonData.payment_id if (jsonData?.payment_id) { - pm.collectionVariables.set("payment_id", jsonData.payment_id); - console.log("- use {{payment_id}} as collection variable for value",jsonData.payment_id); + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id + ); } else { - console.log('INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.'); -}; - + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." + ); +} // pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id if (jsonData?.mandate_id) { - pm.collectionVariables.set("mandate_id", jsonData.mandate_id); - console.log("- use {{mandate_id}} as collection variable for value",jsonData.mandate_id); + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id + ); } else { - console.log('INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.'); -}; + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." + ); +} // pm.collectionVariables - Set client_secret as variable for jsonData.client_secret if (jsonData?.client_secret) { - pm.collectionVariables.set("client_secret", jsonData.client_secret); - console.log("- use {{client_secret}} as collection variable for value",jsonData.client_secret); + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret + ); } else { - console.log('INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.'); -}; + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." + ); +} // Response body should have value "processing" for "status" if (jsonData?.status) { @@ -48,13 +66,18 @@ if (jsonData?.status) { "[POST]::/payments - Content check if value for 'status' matches 'processing'", function () { pm.expect(jsonData.status).to.eql("processing"); - }, + } ); } if (jsonData?.customer_id) { - pm.collectionVariables.set("customer_id", jsonData.customer_id); - console.log("- use {{customer_id}} as collection variable for value",jsonData.customer_id); + pm.collectionVariables.set("customer_id", jsonData.customer_id); + console.log( + "- use {{customer_id}} as collection variable for value", + jsonData.customer_id + ); } else { - console.log('INFO - Unable to assign variable {{customer_id}}, as jsonData.customer_id is undefined.'); -}; \ No newline at end of file + console.log( + "INFO - Unable to assign variable {{customer_id}}, as jsonData.customer_id is undefined." + ); +} diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/.event.meta.json new file mode 100644 index 00000000000..688c85746ef --- /dev/null +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/event.test.js b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/event.test.js new file mode 100644 index 00000000000..814cc4496dd --- /dev/null +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/event.test.js @@ -0,0 +1,61 @@ +// Validate status 2xx +pm.test("[GET]::/payments/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json" + ); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined." + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined." + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined." + ); +} diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/request.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/request.json new file mode 100644 index 00000000000..6cd4b7d96c5 --- /dev/null +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/request.json @@ -0,0 +1,28 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": ["{{baseUrl}}"], + "path": ["payments", ":id"], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/response.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/response.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] From 00e913c75c14a45fdd513b233f67db7edbaf7380 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:46:57 +0530 Subject: [PATCH 38/48] feat(connector): [DEUTSCHEBANK] Implement SEPA recurring payments (#5925) --- crates/api_models/src/payments.rs | 1 + .../src/connectors/deutschebank.rs | 74 +++++-- .../connectors/deutschebank/transformers.rs | 197 +++++++++++++++--- .../src/connectors/fiservemea/transformers.rs | 11 +- .../src/connectors/novalnet/transformers.rs | 1 + .../src/router_data.rs | 1 + .../src/router_response_types.rs | 1 + crates/hyperswitch_interfaces/src/errors.rs | 2 + .../router/src/connector/aci/transformers.rs | 1 + .../src/connector/adyen/transformers.rs | 1 + .../connector/authorizedotnet/transformers.rs | 2 + .../src/connector/bamboraapac/transformers.rs | 2 + .../connector/bankofamerica/transformers.rs | 2 + .../src/connector/braintree/transformers.rs | 4 + .../src/connector/cybersource/transformers.rs | 2 + .../src/connector/globalpay/transformers.rs | 1 + .../src/connector/gocardless/transformers.rs | 2 + .../connector/multisafepay/transformers.rs | 1 + .../src/connector/nexinets/transformers.rs | 1 + .../router/src/connector/noon/transformers.rs | 1 + .../src/connector/nuvei/transformers.rs | 1 + .../src/connector/payeezy/transformers.rs | 1 + .../src/connector/payme/transformers.rs | 1 + .../src/connector/stripe/transformers.rs | 3 + .../src/connector/wellsfargo/transformers.rs | 2 + crates/router/src/core/errors/utils.rs | 3 + crates/router/src/core/payments.rs | 5 + crates/router/src/core/payments/helpers.rs | 2 + .../payments/operations/payment_confirm.rs | 1 + .../payments/operations/payment_create.rs | 4 +- .../payments/operations/payment_response.rs | 14 +- .../payments/operations/payment_update.rs | 2 +- .../router/src/core/payments/tokenization.rs | 20 +- .../src/types/storage/payment_method.rs | 1 + 34 files changed, 300 insertions(+), 68 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index b0866cfb265..535a5abffce 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1042,6 +1042,7 @@ pub struct ConnectorMandateReferenceId { pub connector_mandate_id: Option, pub payment_method_id: Option, pub update_history: Option>, + pub mandate_metadata: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)] diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank.rs index 30e67babe89..a861155afb3 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank.rs @@ -50,7 +50,10 @@ use transformers as deutschebank; use crate::{ constants::headers, types::ResponseRouterData, - utils::{self, PaymentsCompleteAuthorizeRequestData, RefundsRequestData}, + utils::{ + self, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, + RefundsRequestData, + }, }; #[derive(Clone)] @@ -182,6 +185,16 @@ impl ConnectorValidation for Deutschebank { ), } } + + fn validate_mandate_payment( + &self, + pm_type: Option, + pm_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData, + ) -> CustomResult<(), errors::ConnectorError> { + let mandate_supported_pmd = + std::collections::HashSet::from([utils::PaymentMethodDataType::SepaBankDebit]); + utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id()) + } } impl ConnectorIntegration for Deutschebank { @@ -302,13 +315,26 @@ impl ConnectorIntegration CustomResult { - Ok(format!( - "{}/services/v2.1/managedmandate", - self.base_url(connectors) - )) + if req.request.connector_mandate_id().is_none() { + Ok(format!( + "{}/services/v2.1/managedmandate", + self.base_url(connectors) + )) + } else { + let event_id = req.connector_request_reference_id.clone(); + let tx_action = if req.request.is_auto_capture()? { + "authorization" + } else { + "preauthorization" + }; + Ok(format!( + "{}/services/v2.1/payment/event/{event_id}/directdebit/{tx_action}", + self.base_url(connectors) + )) + } } fn get_request_body( @@ -356,17 +382,31 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - let response: deutschebank::DeutschebankMandatePostResponse = res - .response - .parse_struct("Deutschebank PaymentsAuthorizeResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) + if data.request.connector_mandate_id().is_none() { + let response: deutschebank::DeutschebankMandatePostResponse = res + .response + .parse_struct("DeutschebankMandatePostResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } else { + let response: deutschebank::DeutschebankPaymentsResponse = res + .response + .parse_struct("DeutschebankPaymentsAuthorizeResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } } fn get_error_response( diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs index dc146dc38d4..8b3837edd63 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use common_enums::enums; -use common_utils::{pii::Email, types::MinorUnit}; +use common_utils::{ext_traits::ValueExt, pii::Email, types::MinorUnit}; +use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::{BankDebitData, PaymentMethodData}, router_data::{AccessToken, ConnectorAuthType, RouterData}, @@ -13,7 +14,9 @@ use hyperswitch_domain_models::{ CompleteAuthorizeData, PaymentsAuthorizeData, PaymentsCaptureData, PaymentsSyncData, ResponseId, }, - router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, + router_response_types::{ + MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData, + }, types::{ PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData, RefundsRouterData, @@ -26,8 +29,8 @@ use serde::{Deserialize, Serialize}; use crate::{ types::{PaymentsCancelResponseRouterData, RefundsResponseRouterData, ResponseRouterData}, utils::{ - AddressDetailsData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, - RefundsRequestData, RouterData as OtherRouterData, + self, AddressDetailsData, PaymentsAuthorizeRequestData, + PaymentsCompleteAuthorizeRequestData, RefundsRequestData, RouterData as OtherRouterData, }, }; @@ -113,7 +116,7 @@ pub enum DeutschebankSEPAApproval { } #[derive(Debug, Serialize, PartialEq)] -pub struct DeutschebankPaymentsRequest { +pub struct DeutschebankMandatePostRequest { approval_by: DeutschebankSEPAApproval, email_address: Email, iban: Secret, @@ -121,6 +124,13 @@ pub struct DeutschebankPaymentsRequest { last_name: Secret, } +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum DeutschebankPaymentsRequest { + MandatePost(DeutschebankMandatePostRequest), + DirectDebit(DeutschebankDirectDebitRequest), +} + impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>> for DeutschebankPaymentsRequest { @@ -128,16 +138,71 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>> fn try_from( item: &DeutschebankRouterData<&PaymentsAuthorizeRouterData>, ) -> Result { - let billing_address = item.router_data.get_billing_address()?; - match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. }) => Ok(Self { - approval_by: DeutschebankSEPAApproval::Click, - email_address: item.router_data.request.get_email()?, - iban, - first_name: billing_address.get_first_name()?.clone(), - last_name: billing_address.get_last_name()?.clone(), - }), - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + match item + .router_data + .request + .mandate_id + .clone() + .and_then(|mandate_id| mandate_id.mandate_reference_id) + { + None => { + if item.router_data.request.is_mandate_payment() { + match item.router_data.request.payment_method_data.clone() { + PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { + iban, .. + }) => { + let billing_address = item.router_data.get_billing_address()?; + Ok(Self::MandatePost(DeutschebankMandatePostRequest { + approval_by: DeutschebankSEPAApproval::Click, + email_address: item.router_data.request.get_email()?, + iban, + first_name: billing_address.get_first_name()?.clone(), + last_name: billing_address.get_last_name()?.clone(), + })) + } + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("deutschebank"), + ) + .into()), + } + } else { + Err(errors::ConnectorError::MissingRequiredField { + field_name: "setup_future_usage or customer_acceptance.acceptance_type", + } + .into()) + } + } + Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => { + let mandate_metadata: DeutschebankMandateMetadata = mandate_data + .mandate_metadata + .ok_or(errors::ConnectorError::MissingConnectorMandateMetadata)? + .clone() + .parse_value("DeutschebankMandateMetadata") + .change_context(errors::ConnectorError::ParsingFailed)?; + Ok(Self::DirectDebit(DeutschebankDirectDebitRequest { + amount_total: DeutschebankAmount { + amount: item.amount, + currency: item.router_data.request.currency, + }, + means_of_payment: DeutschebankMeansOfPayment { + bank_account: DeutschebankBankAccount { + account_holder: mandate_metadata.account_holder, + iban: mandate_metadata.iban, + }, + }, + mandate: DeutschebankMandate { + reference: mandate_metadata.reference, + signed_on: mandate_metadata.signed_on, + }, + })) + } + Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) + | Some(api_models::payments::MandateReferenceId::NetworkMandateId(_)) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("deutschebank"), + ) + .into()) + } } } } @@ -176,6 +241,14 @@ impl From for common_enums::AttemptStatus { } } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct DeutschebankMandateMetadata { + account_holder: Secret, + iban: Secret, + reference: Secret, + signed_on: String, +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct DeutschebankMandatePostResponse { rc: String, @@ -207,14 +280,14 @@ impl PaymentsResponseData, >, ) -> Result { - let signed_on = match item.response.approval_date { + let signed_on = match item.response.approval_date.clone() { Some(date) => date.chars().take(10).collect(), None => time::OffsetDateTime::now_utc().date().to_string(), }; - match item.response.reference { + match item.response.reference.clone() { Some(reference) => Ok(Self { status: if item.response.rc == "0" { - match item.response.state { + match item.response.state.clone() { Some(state) => common_enums::AttemptStatus::from(state), None => common_enums::AttemptStatus::Failure, } @@ -227,11 +300,29 @@ impl endpoint: item.data.request.get_complete_authorize_url()?, method: common_utils::request::Method::Get, form_fields: HashMap::from([ - ("reference".to_string(), reference), - ("signed_on".to_string(), signed_on), + ("reference".to_string(), reference.clone()), + ("signed_on".to_string(), signed_on.clone()), ]), }), - mandate_reference: None, + mandate_reference: Some(MandateReference { + connector_mandate_id: item.response.mandate_id, + payment_method_id: None, + mandate_metadata: Some(serde_json::json!(DeutschebankMandateMetadata { + account_holder: item.data.get_billing_address()?.get_full_name()?, + iban: match item.data.request.payment_method_data.clone() { + PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { + iban, + .. + }) => Ok(iban), + _ => Err(errors::ConnectorError::MissingRequiredField { + field_name: + "payment_method_data.bank_debit.sepa_bank_debit.iban" + }), + }?, + reference: Secret::from(reference), + signed_on, + })), + }), connector_metadata: None, network_txn_id: None, connector_response_reference_id: None, @@ -248,6 +339,49 @@ impl } } +impl + TryFrom< + ResponseRouterData< + Authorize, + DeutschebankPaymentsResponse, + PaymentsAuthorizeData, + PaymentsResponseData, + >, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + Authorize, + DeutschebankPaymentsResponse, + PaymentsAuthorizeData, + PaymentsResponseData, + >, + ) -> Result { + Ok(Self { + status: if item.response.rc == "0" { + match item.data.request.is_auto_capture()? { + true => common_enums::AttemptStatus::Charged, + false => common_enums::AttemptStatus::Authorized, + } + } else { + common_enums::AttemptStatus::Failure + }, + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.tx_id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct DeutschebankAmount { amount: MinorUnit, @@ -268,7 +402,7 @@ pub struct DeutschebankBankAccount { #[derive(Debug, Serialize, PartialEq)] pub struct DeutschebankMandate { reference: Secret, - signed_on: Secret, + signed_on: String, } #[derive(Debug, Serialize, PartialEq)] @@ -319,14 +453,12 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>> })? .to_owned(), ); - let signed_on = Secret::from( - queries_params - .get("signed_on") - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "signed_on", - })? - .to_owned(), - ); + let signed_on = queries_params + .get("signed_on") + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "signed_on", + })? + .to_owned(); match item.router_data.request.payment_method_data.clone() { Some(PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. })) => { @@ -349,7 +481,10 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>> }, }) } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("deutschebank"), + ) + .into()), } } } diff --git a/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs index fdec3d0e1eb..7f88903e867 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs @@ -170,13 +170,6 @@ pub enum ResponseType { UnsupportedMediaType, } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum FiservemeaResponseType { - TransactionResponse, - ErrorResponse, -} - #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum FiservemeaTransactionType { @@ -329,7 +322,7 @@ fn map_status( pub struct FiservemeaPaymentsResponse { response_type: Option, #[serde(rename = "type")] - fiservemea_type: Option, + fiservemea_type: Option, client_request_id: Option, api_trace_id: Option, ipg_transaction_id: String, @@ -526,7 +519,7 @@ pub struct FiservemeaError { #[derive(Debug, Serialize, Deserialize)] pub struct FiservemeaErrorResponse { #[serde(rename = "type")] - fiservemea_type: Option, + fiservemea_type: Option, client_request_id: Option, api_trace_id: Option, pub response_type: Option, diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs index b400555305d..56da53c2873 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs @@ -787,6 +787,7 @@ impl MandateReference { connector_mandate_id: Some(id.clone()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index df0b63833d9..a9451f6156a 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -242,6 +242,7 @@ pub struct RecurringMandatePaymentData { pub payment_method_type: Option, //required for making recurring payment using saved payment method through stripe pub original_payment_authorized_amount: Option, pub original_payment_authorized_currency: Option, + pub mandate_metadata: Option, } #[derive(Debug, Clone)] diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs index cb682514c83..79a78efb538 100644 --- a/crates/hyperswitch_domain_models/src/router_response_types.rs +++ b/crates/hyperswitch_domain_models/src/router_response_types.rs @@ -82,6 +82,7 @@ pub struct TaxCalculationResponseData { pub struct MandateReference { pub connector_mandate_id: Option, pub payment_method_id: Option, + pub mandate_metadata: Option, } #[derive(Debug, Clone)] diff --git a/crates/hyperswitch_interfaces/src/errors.rs b/crates/hyperswitch_interfaces/src/errors.rs index e36707af6b0..06f197ebf0b 100644 --- a/crates/hyperswitch_interfaces/src/errors.rs +++ b/crates/hyperswitch_interfaces/src/errors.rs @@ -56,6 +56,8 @@ pub enum ConnectorError { CaptureMethodNotSupported, #[error("Missing connector mandate ID")] MissingConnectorMandateID, + #[error("Missing connector mandate metadata")] + MissingConnectorMandateMetadata, #[error("Missing connector transaction ID")] MissingConnectorTransactionID, #[error("Missing connector refund ID")] diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 5842143ad44..46f312b38f2 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -746,6 +746,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index e915d05c358..361ebece299 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -3294,6 +3294,7 @@ pub fn get_adyen_response( .map(|mandate_id| types::MandateReference { connector_mandate_id: Some(mandate_id.expose()), payment_method_id: None, + mandate_metadata: None, }); let network_txn_id = response.additional_data.and_then(|additional_data| { additional_data diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 57563c276f5..868409dafc7 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -398,6 +398,7 @@ impl format!("{customer_profile_id}-{payment_profile_id}") }), payment_method_id: None, + mandate_metadata: None, }, ), connector_metadata: None, @@ -1109,6 +1110,7 @@ impl }, ), payment_method_id: None, + mandate_metadata: None, } }); diff --git a/crates/router/src/connector/bamboraapac/transformers.rs b/crates/router/src/connector/bamboraapac/transformers.rs index 3c197699309..819c918338e 100644 --- a/crates/router/src/connector/bamboraapac/transformers.rs +++ b/crates/router/src/connector/bamboraapac/transformers.rs @@ -280,6 +280,7 @@ impl Some(types::MandateReference { connector_mandate_id, payment_method_id: None, + mandate_metadata: None, }) } else { None @@ -463,6 +464,7 @@ impl mandate_reference: Some(types::MandateReference { connector_mandate_id: Some(connector_mandate_id), payment_method_id: None, + mandate_metadata: None, }), connector_metadata: None, network_txn_id: None, diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index 46a522c7378..f47ab4139e8 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -359,6 +359,7 @@ impl .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, } }); let mut mandate_status = @@ -1487,6 +1488,7 @@ fn get_payment_response( .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(types::PaymentsResponseData::TransactionResponse { diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index 22982ceb213..05d8c1c559d 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -443,6 +443,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, @@ -617,6 +618,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, @@ -698,6 +700,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, @@ -761,6 +764,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index fc35cab773a..9ad4f0604a4 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -2227,6 +2227,7 @@ fn get_payment_response( .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(types::PaymentsResponseData::TransactionResponse { @@ -2947,6 +2948,7 @@ impl .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); let mut mandate_status = enums::AttemptStatus::foreign_from(( item.response diff --git a/crates/router/src/connector/globalpay/transformers.rs b/crates/router/src/connector/globalpay/transformers.rs index efc6661b447..19568ae4671 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -261,6 +261,7 @@ fn get_payment_response( .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }) }); match status { diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index f3250602dbc..ff2f9d95026 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -512,6 +512,7 @@ impl let mandate_reference = Some(MandateReference { connector_mandate_id: Some(item.response.mandates.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { response: Ok(types::PaymentsResponseData::TransactionResponse { @@ -664,6 +665,7 @@ impl let mandate_reference = MandateReference { connector_mandate_id: Some(item.data.request.get_connector_mandate_id()?), payment_method_id: None, + mandate_metadata: None, }; Ok(Self { status: enums::AttemptStatus::from(item.response.payments.status), diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index c44cf14ab65..547a38ad849 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -982,6 +982,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }), connector_metadata: None, network_txn_id: None, diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index 43cb4cecec8..55b2fbe4176 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -358,6 +358,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { status: enums::AttemptStatus::foreign_from(( diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index b7df6829da4..5d987174273 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -574,6 +574,7 @@ impl .map(|subscription_data| types::MandateReference { connector_mandate_id: Some(subscription_data.identifier.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { status, diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs index 2547443484e..f280d3a42a2 100644 --- a/crates/router/src/connector/nuvei/transformers.rs +++ b/crates/router/src/connector/nuvei/transformers.rs @@ -1602,6 +1602,7 @@ where .map(|id| types::MandateReference { connector_mandate_id: Some(id), payment_method_id: None, + mandate_metadata: None, }), // we don't need to save session token for capture, void flow so ignoring if it is not present connector_metadata: if let Some(token) = response.session_token { diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index 0bb10c9845a..8c2ad2ef979 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -418,6 +418,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }); let status = enums::AttemptStatus::foreign_from(( item.response.transaction_status, diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index c12e478b08e..e0ab598d76d 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -250,6 +250,7 @@ impl TryFrom<&PaymePaySaleResponse> for types::PaymentsResponseData { mandate_reference: value.buyer_key.clone().map(|buyer_key| MandateReference { connector_mandate_id: Some(buyer_key.expose()), payment_method_id: None, + mandate_metadata: None, }), connector_metadata: None, network_txn_id: None, diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 910cb94e929..2129ad327c2 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -2389,6 +2389,7 @@ impl types::MandateReference { connector_mandate_id, payment_method_id, + mandate_metadata: None, } }); @@ -2583,6 +2584,7 @@ impl types::MandateReference { connector_mandate_id, payment_method_id: Some(payment_method_id), + mandate_metadata: None, } }); @@ -2673,6 +2675,7 @@ impl types::MandateReference { connector_mandate_id, payment_method_id, + mandate_metadata: None, } }); let status = enums::AttemptStatus::from(item.response.status); diff --git a/crates/router/src/connector/wellsfargo/transformers.rs b/crates/router/src/connector/wellsfargo/transformers.rs index 761fc43982b..be3cf4a1cef 100644 --- a/crates/router/src/connector/wellsfargo/transformers.rs +++ b/crates/router/src/connector/wellsfargo/transformers.rs @@ -1778,6 +1778,7 @@ fn get_payment_response( .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(types::PaymentsResponseData::TransactionResponse { @@ -1963,6 +1964,7 @@ impl .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); let mut mandate_status = enums::AttemptStatus::foreign_from(( item.response diff --git a/crates/router/src/core/errors/utils.rs b/crates/router/src/core/errors/utils.rs index d2812447ed8..549857494f0 100644 --- a/crates/router/src/core/errors/utils.rs +++ b/crates/router/src/core/errors/utils.rs @@ -189,6 +189,7 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::FlowNotSupported { .. } | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID + | errors::ConnectorError::MissingConnectorMandateMetadata | errors::ConnectorError::MissingConnectorTransactionID | errors::ConnectorError::MissingConnectorRefundID | errors::ConnectorError::MissingApplePayTokenData @@ -288,6 +289,7 @@ impl ConnectorErrorExt for error_stack::Result errors::ConnectorError::FailedToObtainCertificateKey | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID | + errors::ConnectorError::MissingConnectorMandateMetadata | errors::ConnectorError::MissingConnectorTransactionID | errors::ConnectorError::MissingConnectorRefundID | errors::ConnectorError::MissingApplePayTokenData | @@ -376,6 +378,7 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::FlowNotSupported { .. } | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID + | errors::ConnectorError::MissingConnectorMandateMetadata | errors::ConnectorError::MissingConnectorTransactionID | errors::ConnectorError::MissingConnectorRefundID | errors::ConnectorError::MissingApplePayTokenData diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 85ae8641373..3a55bf77771 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -4093,6 +4093,9 @@ where payment_method_info.get_id().clone(), ), update_history: None, + mandate_metadata: mandate_reference_record + .mandate_metadata + .clone(), }, )); payment_data.set_recurring_mandate_payment_data( @@ -4103,6 +4106,8 @@ where .original_payment_authorized_amount, original_payment_authorized_currency: mandate_reference_record .original_payment_authorized_currency, + mandate_metadata: mandate_reference_record + .mandate_metadata.clone(), }); connector_choice = Some((connector_data, mandate_reference_id.clone())); diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e9dca13dc80..ac6c236f536 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -802,6 +802,7 @@ pub async fn get_token_for_recurring_mandate( payment_method_type, original_payment_authorized_amount, original_payment_authorized_currency, + mandate_metadata: None, }), payment_method_type: payment_method.payment_method_type, mandate_connector: Some(mandate_connector_details), @@ -816,6 +817,7 @@ pub async fn get_token_for_recurring_mandate( payment_method_type, original_payment_authorized_amount, original_payment_authorized_currency, + mandate_metadata: None, }), payment_method_type: payment_method.payment_method_type, mandate_connector: Some(mandate_connector_details), diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index c75cc29ce51..658a02f7d08 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -681,6 +681,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ), payment_method_id: None, update_history: None, + mandate_metadata: None, }, ), ), diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 0731def9453..4b931d68f1e 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -398,7 +398,8 @@ impl GetTracker, api::PaymentsRequest> for Pa api_models::payments::ConnectorMandateReferenceId{ connector_mandate_id: connector_id.connector_mandate_id, payment_method_id: connector_id.payment_method_id, - update_history: None + update_history: None, + mandate_metadata: None, } )) } @@ -437,6 +438,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ), payment_method_id: None, update_history: None, + mandate_metadata: None, }, ), ), diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 90ca49c9746..09292224cae 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -1504,21 +1504,24 @@ async fn payment_response_update_tracker( let flow_name = core_utils::get_flow_name::()?; if flow_name == "PSync" || flow_name == "CompleteAuthorize" { - let connector_mandate_id = match router_data.response.clone() { + let (connector_mandate_id, mandate_metadata) = match router_data.response.clone() { Ok(resp) => match resp { types::PaymentsResponseData::TransactionResponse { ref mandate_reference, .. } => { if let Some(mandate_ref) = mandate_reference { - mandate_ref.connector_mandate_id.clone() + ( + mandate_ref.connector_mandate_id.clone(), + mandate_ref.mandate_metadata.clone(), + ) } else { - None + (None, None) } } - _ => None, + _ => (None, None), }, - Err(_) => None, + Err(_) => (None, None), }; if let Some(payment_method) = payment_data.payment_method_info.clone() { let connector_mandate_details = @@ -1529,6 +1532,7 @@ async fn payment_response_update_tracker( payment_data.payment_attempt.currency, payment_data.payment_attempt.merchant_connector_id.clone(), connector_mandate_id, + mandate_metadata, )?; payment_methods::cards::update_payment_method_connector_mandate_details( state, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index c5e7cebac34..c69595d1893 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -329,7 +329,7 @@ impl GetTracker, api::PaymentsRequest> for Pa api_models::payments::MandateIds { mandate_id: Some(mandate_obj.mandate_id), mandate_reference_id: Some(api_models::payments::MandateReferenceId::ConnectorMandateId( - api_models::payments::ConnectorMandateReferenceId {connector_mandate_id:connector_id.connector_mandate_id,payment_method_id:connector_id.payment_method_id, update_history: None }, + api_models::payments::ConnectorMandateReferenceId {connector_mandate_id:connector_id.connector_mandate_id,payment_method_id:connector_id.payment_method_id, update_history: None, mandate_metadata:connector_id.mandate_metadata, }, )) } }), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 2d81aecb30e..ca8c6718aa7 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -148,18 +148,21 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to serialize customer acceptance to value")?; - let connector_mandate_id = match responses { + let (connector_mandate_id, mandate_metadata) = match responses { types::PaymentsResponseData::TransactionResponse { ref mandate_reference, .. } => { if let Some(mandate_ref) = mandate_reference { - mandate_ref.connector_mandate_id.clone() + ( + mandate_ref.connector_mandate_id.clone(), + mandate_ref.mandate_metadata.clone(), + ) } else { - None + (None, None) } } - _ => None, + _ => (None, None), }; let check_for_mit_mandates = save_payment_method_data .request @@ -178,6 +181,7 @@ where currency, merchant_connector_id.clone(), connector_mandate_id.clone(), + mandate_metadata.clone(), ) } else { None @@ -373,6 +377,7 @@ where currency, merchant_connector_id.clone(), connector_mandate_id.clone(), + mandate_metadata.clone(), )?; payment_methods::cards::update_payment_method_connector_mandate_details(state, @@ -479,6 +484,7 @@ where currency, merchant_connector_id.clone(), connector_mandate_id.clone(), + mandate_metadata.clone(), )?; payment_methods::cards::update_payment_method_connector_mandate_details( state, @@ -1160,6 +1166,7 @@ pub fn add_connector_mandate_details_in_payment_method( authorized_currency: Option, merchant_connector_id: Option, connector_mandate_id: Option, + mandate_metadata: Option, ) -> Option { let mut mandate_details = HashMap::new(); @@ -1173,6 +1180,7 @@ pub fn add_connector_mandate_details_in_payment_method( payment_method_type, original_payment_authorized_amount: authorized_amount, original_payment_authorized_currency: authorized_currency, + mandate_metadata, }, ); Some(storage::PaymentsMandateReference(mandate_details)) @@ -1188,6 +1196,7 @@ pub fn update_connector_mandate_details_in_payment_method( authorized_currency: Option, merchant_connector_id: Option, connector_mandate_id: Option, + mandate_metadata: Option, ) -> RouterResult> { let mandate_reference = match payment_method.connector_mandate_details { Some(_) => { @@ -1208,6 +1217,7 @@ pub fn update_connector_mandate_details_in_payment_method( payment_method_type, original_payment_authorized_amount: authorized_amount, original_payment_authorized_currency: authorized_currency, + mandate_metadata: mandate_metadata.clone(), }; mandate_details.map(|mut payment_mandate_reference| { payment_mandate_reference @@ -1218,6 +1228,7 @@ pub fn update_connector_mandate_details_in_payment_method( payment_method_type, original_payment_authorized_amount: authorized_amount, original_payment_authorized_currency: authorized_currency, + mandate_metadata: mandate_metadata.clone(), }); payment_mandate_reference }) @@ -1231,6 +1242,7 @@ pub fn update_connector_mandate_details_in_payment_method( authorized_currency, merchant_connector_id, connector_mandate_id, + mandate_metadata, ), }; let connector_mandate_details = mandate_reference diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 34c5714e126..bb1b801edb6 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -124,6 +124,7 @@ pub struct PaymentsMandateReferenceRecord { pub payment_method_type: Option, pub original_payment_authorized_amount: Option, pub original_payment_authorized_currency: Option, + pub mandate_metadata: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] From 5335f2d21ce6f0c11dc84fd56b3cd2c80e8b064f Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:50:53 +0530 Subject: [PATCH 39/48] refactor(payment_intent_v2): payment intent fields refactoring (#5880) Co-authored-by: hrithikesh026 Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/payments.rs | 154 +- crates/common_enums/src/enums.rs | 36 + crates/common_enums/src/transformers.rs | 73 + crates/common_utils/src/consts.rs | 5 + crates/common_utils/src/id_type.rs | 8 +- crates/common_utils/src/id_type/global_id.rs | 17 +- .../src/id_type/global_id/payment.rs | 63 + .../{ => global_id}/payment_methods.rs | 14 +- crates/common_utils/src/id_type/payment.rs | 17 +- crates/common_utils/src/types.rs | 398 ++++- crates/diesel_models/src/payment_intent.rs | 1274 +++++++++-------- .../diesel_models/src/query/payment_intent.rs | 4 +- crates/diesel_models/src/schema_v2.rs | 25 +- .../hyperswitch_domain_models/src/payments.rs | 172 ++- .../src/payments/payment_intent.rs | 1208 ++++++++-------- crates/router/src/core/customers.rs | 4 +- crates/router/src/core/fraud_check.rs | 50 + .../fraud_check/flows/fulfillment_flow.rs | 14 + .../fraud_check/operation/fraud_check_post.rs | 11 + .../fraud_check/operation/fraud_check_pre.rs | 12 + crates/router/src/core/payment_methods.rs | 20 +- .../router/src/core/payment_methods/cards.rs | 1 + .../src/core/payment_methods/transformers.rs | 2 +- crates/router/src/core/payments.rs | 55 +- crates/router/src/core/payments/helpers.rs | 62 +- .../payments/operations/payment_response.rs | 16 + crates/router/src/core/payments/retry.rs | 10 +- crates/router/src/core/payments/routing.rs | 34 +- .../router/src/core/payments/transformers.rs | 32 + crates/router/src/core/pm_auth.rs | 19 +- crates/router/src/core/utils.rs | 42 + crates/router/src/core/verification/utils.rs | 22 +- crates/router/src/db/kafka_store.rs | 2 +- .../src/services/kafka/payment_intent.rs | 79 +- .../services/kafka/payment_intent_event.rs | 79 +- crates/router/src/types/storage.rs | 2 +- crates/router/src/utils.rs | 1 + crates/storage_impl/src/lib.rs | 16 +- .../src/mock_db/payment_intent.rs | 2 +- .../src/payments/payment_intent.rs | 65 +- .../2024-08-28-081721_add_v2_columns/down.sql | 8 +- .../2024-08-28-081721_add_v2_columns/up.sql | 16 +- .../down.sql | 17 + .../up.sql | 11 + .../down.sql | 9 +- .../2024-08-28-081847_drop_v1_columns/up.sql | 9 +- .../down.sql | 2 - .../up.sql | 2 - 48 files changed, 2526 insertions(+), 1668 deletions(-) create mode 100644 crates/common_utils/src/id_type/global_id/payment.rs rename crates/common_utils/src/id_type/{ => global_id}/payment_methods.rs (86%) delete mode 100644 v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql delete mode 100644 v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 535a5abffce..d1b658dab4e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -18,11 +18,7 @@ use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret, SwitchStrategy, WithType}; use router_derive::Setter; use rustc_hash::FxHashMap; -use serde::{ - de::{self, Unexpected, Visitor}, - ser::Serializer, - Deserialize, Deserializer, Serialize, -}; +use serde::{de, ser::Serializer, Deserialize, Deserializer, Serialize}; use strum::Display; use time::{Date, PrimitiveDateTime}; use url::Url; @@ -68,154 +64,6 @@ pub struct BankCodeResponse { pub eligible_connectors: Vec, } -#[derive(Debug, PartialEq, Clone)] -pub struct ClientSecret { - pub payment_id: id_type::PaymentId, - pub secret: String, -} - -impl ClientSecret { - pub fn get_client_secret(&self) -> String { - format!( - "{}_secret_{}", - self.payment_id.get_string_repr(), - self.secret - ) - } -} - -impl<'de> Deserialize<'de> for ClientSecret { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct ClientSecretVisitor; - - impl<'de> Visitor<'de> for ClientSecretVisitor { - type Value = ClientSecret; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("a string in the format '{payment_id}_secret_{secret}'") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - let (payment_id, secret) = value.rsplit_once("_secret_").ok_or_else(|| { - E::invalid_value(Unexpected::Str(value), &"a string with '_secret_'") - })?; - - let payment_id = - id_type::PaymentId::try_from(std::borrow::Cow::Owned(payment_id.to_owned())) - .map_err(de::Error::custom)?; - - Ok(ClientSecret { - payment_id, - secret: secret.to_owned(), - }) - } - } - - deserializer.deserialize_str(ClientSecretVisitor) - } -} - -impl Serialize for ClientSecret { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let combined = format!( - "{}_secret_{}", - self.payment_id.get_string_repr(), - self.secret - ); - serializer.serialize_str(&combined) - } -} - -#[cfg(test)] -mod client_secret_tests { - #![allow(clippy::expect_used)] - #![allow(clippy::unwrap_used)] - - use serde_json; - - use super::*; - - #[test] - fn test_serialize_client_secret() { - let client_secret1 = ClientSecret { - payment_id: id_type::PaymentId::try_from(std::borrow::Cow::Borrowed( - "pay_3TgelAms4RQec8xSStjF", - )) - .unwrap(), - secret: "fc34taHLw1ekPgNh92qr".to_string(), - }; - let client_secret2 = ClientSecret { - payment_id: id_type::PaymentId::try_from(std::borrow::Cow::Borrowed( - "pay_3Tgel__Ams4RQ_secret_ec8xSStjF", - )) - .unwrap(), - secret: "fc34taHLw1ekPgNh92qr".to_string(), - }; - - let expected_str1 = r#""pay_3TgelAms4RQec8xSStjF_secret_fc34taHLw1ekPgNh92qr""#; - let expected_str2 = r#""pay_3Tgel__Ams4RQ_secret_ec8xSStjF_secret_fc34taHLw1ekPgNh92qr""#; - - let actual_str1 = - serde_json::to_string(&client_secret1).expect("Failed to serialize client_secret1"); - let actual_str2 = - serde_json::to_string(&client_secret2).expect("Failed to serialize client_secret2"); - - assert_eq!(expected_str1, actual_str1); - assert_eq!(expected_str2, actual_str2); - } - - #[test] - fn test_deserialize_client_secret() { - let client_secret_str1 = r#""pay_3TgelAms4RQec8xSStjF_secret_fc34taHLw1ekPgNh92qr""#; - let client_secret_str2 = - r#""pay_3Tgel__Ams4RQ_secret_ec8xSStjF_secret_fc34taHLw1ekPgNh92qr""#; - let client_secret_str3 = - r#""pay_3Tgel__Ams4RQ_secret_ec8xSStjF_secret__secret_fc34taHLw1ekPgNh92qr""#; - - let expected1 = ClientSecret { - payment_id: id_type::PaymentId::try_from(std::borrow::Cow::Borrowed( - "pay_3TgelAms4RQec8xSStjF", - )) - .unwrap(), - secret: "fc34taHLw1ekPgNh92qr".to_string(), - }; - let expected2 = ClientSecret { - payment_id: id_type::PaymentId::try_from(std::borrow::Cow::Borrowed( - "pay_3Tgel__Ams4RQ_secret_ec8xSStjF", - )) - .unwrap(), - secret: "fc34taHLw1ekPgNh92qr".to_string(), - }; - let expected3 = ClientSecret { - payment_id: id_type::PaymentId::try_from(std::borrow::Cow::Borrowed( - "pay_3Tgel__Ams4RQ_secret_ec8xSStjF_secret_", - )) - .unwrap(), - secret: "fc34taHLw1ekPgNh92qr".to_string(), - }; - - let actual1: ClientSecret = serde_json::from_str(client_secret_str1) - .expect("Failed to deserialize client_secret_str1"); - let actual2: ClientSecret = serde_json::from_str(client_secret_str2) - .expect("Failed to deserialize client_secret_str2"); - let actual3: ClientSecret = serde_json::from_str(client_secret_str3) - .expect("Failed to deserialize client_secret_str3"); - - assert_eq!(expected1, actual1); - assert_eq!(expected2, actual2); - assert_eq!(expected3, actual3); - } -} - /// Passing this object creates a new customer or attaches an existing customer to the payment #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema, PartialEq)] pub struct CustomerDetails { diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 31cb43669b2..65ef8d87255 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3213,3 +3213,39 @@ pub enum DeleteStatus { Active, Redacted, } + +/// Whether 3ds authentication is requested or not +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub enum External3dsAuthenticationRequest { + /// Request for 3ds authentication + Enable, + /// Skip 3ds authentication + Skip, +} + +/// Whether payment link is requested to be enabled or not for this transaction +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub enum EnablePaymentLinkRequest { + /// Request for enabling payment link + Enable, + /// Skip enabling payment link + Skip, +} + +/// Whether mit exemption is requested or not +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub enum MitExemptionRequest { + /// Request for applying MIT exemption + Apply, + /// Skip applying MIT exemption + Skip, +} + +/// Whether customer is present / absent during the payment +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub enum PresenceOfCustomerDuringPayment { + /// Customer is present during the payment. This is the default value + Present, + /// Customer is absent during the payment + Absent, +} diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs index fab570542c8..db4162a8bb1 100644 --- a/crates/common_enums/src/transformers.rs +++ b/crates/common_enums/src/transformers.rs @@ -1991,6 +1991,79 @@ mod custom_serde { } } +impl From> for super::External3dsAuthenticationRequest { + fn from(value: Option) -> Self { + match value { + Some(true) => Self::Enable, + _ => Self::Skip, + } + } +} + +/// Get the boolean value of the `External3dsAuthenticationRequest`. +impl super::External3dsAuthenticationRequest { + pub fn as_bool(&self) -> bool { + match self { + Self::Enable => true, + Self::Skip => false, + } + } +} + +impl super::EnablePaymentLinkRequest { + pub fn as_bool(&self) -> bool { + match self { + Self::Enable => true, + Self::Skip => false, + } + } +} + +impl From> for super::EnablePaymentLinkRequest { + fn from(value: Option) -> Self { + match value { + Some(true) => Self::Enable, + _ => Self::Skip, + } + } +} + +impl From> for super::MitExemptionRequest { + fn from(value: Option) -> Self { + match value { + Some(true) => Self::Apply, + _ => Self::Skip, + } + } +} + +impl super::MitExemptionRequest { + pub fn as_bool(&self) -> bool { + match self { + Self::Apply => true, + Self::Skip => false, + } + } +} + +impl From> for super::PresenceOfCustomerDuringPayment { + fn from(value: Option) -> Self { + match value { + Some(false) => Self::Absent, + _ => Self::Present, + } + } +} + +impl super::PresenceOfCustomerDuringPayment { + pub fn as_bool(&self) -> bool { + match self { + Self::Present => true, + Self::Absent => false, + } + } +} + #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 70f00ad9b3f..ce9a3b2caef 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -147,5 +147,10 @@ pub const ROLE_ID_INTERNAL_VIEW_ONLY_USER: &str = "internal_view_only"; /// Role ID for Internal Admin pub const ROLE_ID_INTERNAL_ADMIN: &str = "internal_admin"; +/// Max length allowed for Description +pub const MAX_DESCRIPTION_LENGTH: u16 = 255; + +/// Max length allowed for Statement Descriptor +pub const MAX_STATEMENT_DESCRIPTOR_LENGTH: u16 = 255; /// Payout flow identifier used for performing GSM operations pub const PAYOUT_FLOW_STR: &str = "payout_flow"; diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index 20dc423310a..26d2c0cdf62 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -11,9 +11,8 @@ mod payment; mod profile; mod routing; +#[cfg(feature = "v2")] mod global_id; -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -mod payment_methods; pub use customer::CustomerId; use diesel::{ @@ -23,12 +22,12 @@ use diesel::{ serialize::{Output, ToSql}, sql_types, }; +#[cfg(feature = "v2")] +pub use global_id::{payment::GlobalPaymentId, payment_methods::GlobalPaymentMethodId, CellId}; pub use merchant::MerchantId; pub use merchant_connector_account::MerchantConnectorAccountId; pub use organization::OrganizationId; pub use payment::PaymentId; -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub use payment_methods::GlobalPaymentMethodId; pub use profile::ProfileId; pub use routing::RoutingId; use serde::{Deserialize, Serialize}; @@ -155,6 +154,7 @@ impl LengthId); +pub struct CellId(LengthId); #[derive(Debug, Error, PartialEq, Eq)] pub enum CellIdError { @@ -107,7 +110,9 @@ impl GlobalId { Self(LengthId::new_unchecked(alphanumeric_id)) } - pub fn from_string(input_string: String) -> Result { + pub(crate) fn from_string( + input_string: std::borrow::Cow<'static, str>, + ) -> Result { let length_id = LengthId::from(input_string.into())?; let input_string = &length_id.0 .0; let (cell_id, remaining) = input_string @@ -118,6 +123,10 @@ impl GlobalId { Ok(Self(length_id)) } + + pub(crate) fn get_string_repr(&self) -> &str { + &self.0 .0 .0 + } } impl ToSql for GlobalId @@ -154,7 +163,7 @@ impl<'de> serde::Deserialize<'de> for GlobalId { D: serde::Deserializer<'de>, { let deserialized_string = String::deserialize(deserializer)?; - Self::from_string(deserialized_string).map_err(serde::de::Error::custom) + Self::from_string(deserialized_string.into()).map_err(serde::de::Error::custom) } } @@ -187,7 +196,7 @@ mod global_id_tests { #[test] fn test_global_id_from_string() { let input_string = "12345_cus_abcdefghijklmnopqrstuvwxyz1234567890"; - let global_id = GlobalId::from_string(input_string.to_string()).unwrap(); + let global_id = GlobalId::from_string(input_string.into()).unwrap(); assert_eq!(global_id.0 .0 .0, input_string); } diff --git a/crates/common_utils/src/id_type/global_id/payment.rs b/crates/common_utils/src/id_type/global_id/payment.rs new file mode 100644 index 00000000000..edc7e3fb960 --- /dev/null +++ b/crates/common_utils/src/id_type/global_id/payment.rs @@ -0,0 +1,63 @@ +use crate::errors; + +/// A global id that can be used to identify a payment +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + serde::Serialize, + serde::Deserialize, + diesel::expression::AsExpression, +)] +#[diesel(sql_type = diesel::sql_types::Text)] +pub struct GlobalPaymentId(super::GlobalId); + +// Database related implementations so that this field can be used directly in the database tables +crate::impl_queryable_id_type!(GlobalPaymentId); + +impl GlobalPaymentId { + /// Get string representation of the id + pub fn get_string_repr(&self) -> &str { + self.0.get_string_repr() + } +} + +// TODO: refactor the macro to include this id use case as well +impl TryFrom> for GlobalPaymentId { + type Error = error_stack::Report; + fn try_from(value: std::borrow::Cow<'static, str>) -> Result { + use error_stack::ResultExt; + let merchant_ref_id = super::GlobalId::from_string(value).change_context( + errors::ValidationError::IncorrectValueProvided { + field_name: "payment_id", + }, + )?; + Ok(Self(merchant_ref_id)) + } +} + +// TODO: refactor the macro to include this id use case as well +impl diesel::serialize::ToSql for GlobalPaymentId +where + DB: diesel::backend::Backend, + super::GlobalId: diesel::serialize::ToSql, +{ + fn to_sql<'b>( + &'b self, + out: &mut diesel::serialize::Output<'b, '_, DB>, + ) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +impl diesel::deserialize::FromSql for GlobalPaymentId +where + DB: diesel::backend::Backend, + super::GlobalId: diesel::deserialize::FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + super::GlobalId::from_sql(value).map(Self) + } +} diff --git a/crates/common_utils/src/id_type/payment_methods.rs b/crates/common_utils/src/id_type/global_id/payment_methods.rs similarity index 86% rename from crates/common_utils/src/id_type/payment_methods.rs rename to crates/common_utils/src/id_type/global_id/payment_methods.rs index f5cc2150913..5ccb774f6ea 100644 --- a/crates/common_utils/src/id_type/payment_methods.rs +++ b/crates/common_utils/src/id_type/global_id/payment_methods.rs @@ -27,9 +27,6 @@ pub enum GlobalPaymentMethodIdError { } impl GlobalPaymentMethodId { - fn get_global_id(&self) -> &GlobalId { - &self.0 - } /// Create a new GlobalPaymentMethodId from celll id information pub fn generate(cell_id: &str) -> error_stack::Result { let cell_id = CellId::from_string(cell_id.to_string())?; @@ -37,18 +34,18 @@ impl GlobalPaymentMethodId { Ok(Self(global_id)) } - pub fn get_string_repr(&self) -> String { - todo!() + pub fn get_string_repr(&self) -> &str { + self.0.get_string_repr() } pub fn generate_from_string(value: String) -> CustomResult { - let id = GlobalId::from_string(value) + let id = GlobalId::from_string(value.into()) .change_context(GlobalPaymentMethodIdError::ConstructionError)?; Ok(Self(id)) } } -impl diesel::Queryable for GlobalPaymentMethodId +impl diesel::Queryable for GlobalPaymentMethodId where DB: diesel::backend::Backend, Self: diesel::deserialize::FromSql, @@ -68,8 +65,7 @@ where &'b self, out: &mut diesel::serialize::Output<'b, '_, DB>, ) -> diesel::serialize::Result { - let id = self.get_global_id(); - id.to_sql(out) + self.0.to_sql(out) } } diff --git a/crates/common_utils/src/id_type/payment.rs b/crates/common_utils/src/id_type/payment.rs index e0269f2beff..c919076e9ce 100644 --- a/crates/common_utils/src/id_type/payment.rs +++ b/crates/common_utils/src/id_type/payment.rs @@ -1,7 +1,7 @@ use crate::{ errors::{CustomResult, ValidationError}, generate_id_with_default_len, - id_type::{global_id, AlphaNumericId, LengthId}, + id_type::{AlphaNumericId, LengthId}, }; crate::id_type!( @@ -15,23 +15,10 @@ crate::impl_debug_id_type!(PaymentId); crate::impl_default_id_type!(PaymentId, "pay"); crate::impl_try_from_cow_str_id_type!(PaymentId, "payment_id"); +// Database related implementations so that this field can be used directly in the database tables crate::impl_queryable_id_type!(PaymentId); crate::impl_to_sql_from_sql_id_type!(PaymentId); -/// A global id that can be used to identify a payment -#[derive( - Debug, - Clone, - Hash, - PartialEq, - Eq, - serde::Serialize, - serde::Deserialize, - diesel::expression::AsExpression, -)] -#[diesel(sql_type = diesel::sql_types::Text)] -pub struct PaymentGlobalId(global_id::GlobalId); - impl PaymentId { /// Get the hash key to be stored in redis pub fn get_hash_key_for_kv_store(&self) -> String { diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 2206744ef0b..9906f3c9114 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -5,6 +5,7 @@ pub mod keymanager; pub mod authentication; use std::{ + borrow::Cow, fmt::Display, ops::{Add, Sub}, primitive::i64, @@ -28,13 +29,16 @@ use rust_decimal::{ }; use semver::Version; use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; +use thiserror::Error; use time::PrimitiveDateTime; use utoipa::ToSchema; use crate::{ - consts, + consts::{self, MAX_DESCRIPTION_LENGTH, MAX_STATEMENT_DESCRIPTOR_LENGTH}, errors::{CustomResult, ParsingError, PercentageError, ValidationError}, + fp_utils::when, }; + /// Represents Percentage Value between 0 and 100 both inclusive #[derive(Clone, Default, Debug, PartialEq, Serialize)] pub struct Percentage { @@ -583,6 +587,251 @@ impl StringMajorUnit { } } +#[derive( + Debug, + serde::Deserialize, + AsExpression, + serde::Serialize, + Clone, + PartialEq, + Eq, + Hash, + ToSchema, + PartialOrd, +)] +#[diesel(sql_type = sql_types::Text)] +/// This domain type can be used for any url +pub struct Url(url::Url); + +impl ToSql for Url +where + DB: Backend, + str: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + let url_string = self.0.as_str(); + url_string.to_sql(out) + } +} + +impl FromSql for Url +where + DB: Backend, + String: FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result { + let val = String::from_sql(value)?; + let url = url::Url::parse(&val)?; + Ok(Self(url)) + } +} + +#[cfg(feature = "v2")] +pub use client_secret_type::ClientSecret; +#[cfg(feature = "v2")] +mod client_secret_type { + use std::fmt; + + use masking::PeekInterface; + + use super::*; + use crate::id_type; + + /// A domain type that can be used to represent a client secret + /// Client secret is generated for a payment and is used to authenticate the client side api calls + #[derive(Debug, PartialEq, Clone, AsExpression)] + #[diesel(sql_type = sql_types::Text)] + pub struct ClientSecret { + /// The payment id of the payment + pub payment_id: id_type::GlobalPaymentId, + /// The secret string + pub secret: masking::Secret, + } + + impl ClientSecret { + pub(crate) fn get_string_repr(&self) -> String { + format!( + "{}_secret_{}", + self.payment_id.get_string_repr(), + self.secret.peek() + ) + } + } + + impl<'de> Deserialize<'de> for ClientSecret { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct ClientSecretVisitor; + + impl<'de> Visitor<'de> for ClientSecretVisitor { + type Value = ClientSecret; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a string in the format '{payment_id}_secret_{secret}'") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + let (payment_id, secret) = value.rsplit_once("_secret_").ok_or_else(|| { + E::invalid_value( + serde::de::Unexpected::Str(value), + &"a string with '_secret_'", + ) + })?; + + let payment_id = + id_type::GlobalPaymentId::try_from(Cow::Owned(payment_id.to_owned())) + .map_err(serde::de::Error::custom)?; + + Ok(ClientSecret { + payment_id, + secret: masking::Secret::new(secret.to_owned()), + }) + } + } + + deserializer.deserialize_str(ClientSecretVisitor) + } + } + + impl Serialize for ClientSecret { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + serializer.serialize_str(self.get_string_repr().as_str()) + } + } + + impl ToSql for ClientSecret + where + String: ToSql, + { + fn to_sql<'b>( + &'b self, + out: &mut Output<'b, '_, diesel::pg::Pg>, + ) -> diesel::serialize::Result { + let string_repr = self.get_string_repr(); + >::to_sql( + &string_repr, + &mut out.reborrow(), + ) + } + } + + impl FromSql for ClientSecret + where + DB: Backend, + String: FromSql, + { + fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result { + let string_repr = String::from_sql(value)?; + Ok(serde_json::from_str(&string_repr)?) + } + } + + impl Queryable for ClientSecret + where + DB: Backend, + Self: FromSql, + { + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } + } + + #[cfg(test)] + mod client_secret_tests { + #![allow(clippy::expect_used)] + #![allow(clippy::unwrap_used)] + + use serde_json; + + use super::*; + use crate::id_type::GlobalPaymentId; + + #[test] + fn test_serialize_client_secret() { + let global_payment_id = "12345_pay_1a961ed9093c48b09781bf8ab17ba6bd"; + let secret = "fc34taHLw1ekPgNh92qr".to_string(); + + let expected_client_secret_string = format!("\"{global_payment_id}_secret_{secret}\""); + + let client_secret1 = ClientSecret { + payment_id: GlobalPaymentId::try_from(Cow::Borrowed(global_payment_id)).unwrap(), + secret: masking::Secret::new(secret), + }; + + let parsed_client_secret = + serde_json::to_string(&client_secret1).expect("Failed to serialize client_secret1"); + + assert_eq!(expected_client_secret_string, parsed_client_secret); + } + + #[test] + fn test_deserialize_client_secret() { + // This is a valid global id + let global_payment_id_str = "12345_pay_1a961ed9093c48b09781bf8ab17ba6bd"; + let secret = "fc34taHLw1ekPgNh92qr".to_string(); + + let valid_payment_global_id = + GlobalPaymentId::try_from(Cow::Borrowed(global_payment_id_str)) + .expect("Failed to create valid global payment id"); + + // This is an invalid global id because of the cell id being in invalid length + let invalid_global_payment_id = "123_pay_1a961ed9093c48b09781bf8ab17ba6bd"; + + // Create a client secret string which is valid + let valid_client_secret = format!(r#""{global_payment_id_str}_secret_{secret}""#); + + dbg!(&valid_client_secret); + + // Create a client secret string which is invalid + let invalid_client_secret_because_of_invalid_payment_id = + format!(r#""{invalid_global_payment_id}_secret_{secret}""#); + + // Create a client secret string which is invalid because of invalid secret + let invalid_client_secret_because_of_invalid_secret = + format!(r#""{invalid_global_payment_id}""#); + + let valid_client_secret = serde_json::from_str::(&valid_client_secret) + .expect("Failed to deserialize client_secret_str1"); + + let invalid_deser1 = serde_json::from_str::( + &invalid_client_secret_because_of_invalid_payment_id, + ); + + dbg!(&invalid_deser1); + + let invalid_deser2 = serde_json::from_str::( + &invalid_client_secret_because_of_invalid_secret, + ); + + dbg!(&invalid_deser2); + + assert_eq!(valid_client_secret.payment_id, valid_payment_global_id); + + assert_eq!(valid_client_secret.secret.peek(), &secret); + + assert_eq!( + invalid_deser1.err().unwrap().to_string(), + "Incorrect value provided for field: payment_id at line 1 column 70" + ); + + assert_eq!( + invalid_deser2.err().unwrap().to_string(), + "invalid value: string \"123_pay_1a961ed9093c48b09781bf8ab17ba6bd\", expected a string with '_secret_' at line 1 column 42" + ); + } + } +} + /// A type representing a range of time for filtering, including a mandatory start time and an optional end time. #[derive( Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema, @@ -725,32 +974,108 @@ pub struct ChargeRefunds { crate::impl_to_sql_from_sql_json!(ChargeRefunds); -/// Domain type for description -#[derive( - Debug, Clone, PartialEq, Eq, Queryable, serde::Deserialize, serde::Serialize, AsExpression, -)] +/// A common type of domain type that can be used for fields that contain a string with restriction of length +#[derive(Debug, Clone, Serialize, Hash, PartialEq, Eq, AsExpression)] #[diesel(sql_type = sql_types::Text)] -pub struct Description(String); +pub(crate) struct LengthString(String); + +/// Error generated from violation of constraints for MerchantReferenceId +#[derive(Debug, Error, PartialEq, Eq)] +pub(crate) enum LengthStringError { + #[error("the maximum allowed length for this field is {0}")] + /// Maximum length of string violated + MaxLengthViolated(u16), + + #[error("the minimum required length for this field is {0}")] + /// Minimum length of string violated + MinLengthViolated(u8), +} -impl Description { - /// Create a new Description Domain type - pub fn new(value: String) -> Self { - Self(value) +impl LengthString { + /// Generates new [MerchantReferenceId] from the given input string + pub fn from(input_string: Cow<'static, str>) -> Result { + let trimmed_input_string = input_string.trim().to_string(); + let length_of_input_string = u16::try_from(trimmed_input_string.len()) + .map_err(|_| LengthStringError::MaxLengthViolated(MAX_LENGTH))?; + + when(length_of_input_string > MAX_LENGTH, || { + Err(LengthStringError::MaxLengthViolated(MAX_LENGTH)) + })?; + + when(length_of_input_string < u16::from(MIN_LENGTH), || { + Err(LengthStringError::MinLengthViolated(MIN_LENGTH)) + })?; + + Ok(Self(trimmed_input_string)) + } + + pub(crate) fn new_unchecked(input_string: String) -> Self { + Self(input_string) + } +} + +impl<'de, const MAX_LENGTH: u16, const MIN_LENGTH: u8> Deserialize<'de> + for LengthString +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let deserialized_string = String::deserialize(deserializer)?; + Self::from(deserialized_string.into()).map_err(serde::de::Error::custom) + } +} + +impl FromSql for LengthString +where + DB: Backend, + String: FromSql, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { + let val = String::from_sql(bytes)?; + Ok(Self(val)) + } +} + +impl ToSql + for LengthString +where + DB: Backend, + String: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) } } -impl From for String { - fn from(description: Description) -> Self { - description.0 +impl Queryable for LengthString +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) } } -impl From for Description { - fn from(description: String) -> Self { - Self(description) +/// Domain type for description +#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, AsExpression)] +#[diesel(sql_type = sql_types::Text)] +pub struct Description(LengthString); + +impl Description { + /// Create a new Description Domain type without any length check from a static str + pub fn from_str_unchecked(input_str: &'static str) -> Self { + Self(LengthString::new_unchecked(input_str.to_owned())) } } +/// Domain type for Statement Descriptor +#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, AsExpression)] +#[diesel(sql_type = sql_types::Text)] +pub struct StatementDescriptor(LengthString); + impl Queryable for Description where DB: Backend, @@ -766,18 +1091,51 @@ where impl FromSql for Description where DB: Backend, - String: FromSql, + LengthString: FromSql, { fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { - let val = String::from_sql(bytes)?; - Ok(Self::from(val)) + let val = LengthString::::from_sql(bytes)?; + Ok(Self(val)) } } impl ToSql for Description where DB: Backend, - String: ToSql, + LengthString: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +impl Queryable for StatementDescriptor +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } +} + +impl FromSql for StatementDescriptor +where + DB: Backend, + LengthString: FromSql, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { + let val = LengthString::::from_sql(bytes)?; + Ok(Self(val)) + } +} + +impl ToSql for StatementDescriptor +where + DB: Backend, + LengthString: ToSql, { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { self.0.to_sql(out) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 42424c9bfff..c6ebbe671e9 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -17,13 +17,12 @@ pub struct PaymentIntent { pub merchant_id: common_utils::id_type::MerchantId, pub status: storage_enums::IntentStatus, pub amount: MinorUnit, - pub currency: Option, + pub currency: storage_enums::Currency, pub amount_captured: Option, pub customer_id: Option, - pub description: Option, - pub return_url: Option, - pub metadata: Option, - pub statement_descriptor_name: Option, + pub description: Option, + pub return_url: Option, + pub metadata: Option, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601")] @@ -31,48 +30,46 @@ pub struct PaymentIntent { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub setup_future_usage: Option, - pub off_session: Option, - pub client_secret: Option, + pub client_secret: common_utils::types::ClientSecret, pub active_attempt_id: String, #[diesel(deserialize_as = super::OptionalDieselArray)] pub order_details: Option>, - pub allowed_payment_method_types: Option, - pub connector_metadata: Option, - pub feature_metadata: Option, + pub allowed_payment_method_types: Option, + pub connector_metadata: Option, + pub feature_metadata: Option, pub attempt_count: i16, - pub profile_id: Option, + pub profile_id: common_utils::id_type::ProfileId, pub payment_link_id: Option, - pub payment_confirm_source: Option, - pub updated_by: String, pub surcharge_applicable: Option, pub request_incremental_authorization: Option, pub authorization_count: Option, pub session_expiry: Option, pub request_external_three_ds_authentication: Option, - pub charges: Option, pub frm_metadata: Option, pub customer_details: Option, - pub merchant_order_reference_id: Option, - pub is_payment_processor_token_flow: Option, pub shipping_cost: Option, pub organization_id: common_utils::id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, - pub merchant_reference_id: String, + pub merchant_reference_id: Option, pub billing_address: Option, pub shipping_address: Option, pub capture_method: Option, pub authentication_type: Option, - pub amount_to_capture: Option, pub prerouting_algorithm: Option, pub surcharge_amount: Option, pub tax_on_surcharge: Option, // Denotes the action(approve or reject) taken by merchant in case of manual review. // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment - pub frm_merchant_decision: Option, - // TODO: change this to global id - pub id: common_utils::id_type::PaymentId, + pub frm_merchant_decision: Option, + pub statement_descriptor: Option, + pub enable_payment_link: Option, + pub apply_mit_exemption: Option, + pub customer_present: Option, + pub routing_algorithm_id: Option, + pub payment_link_config: Option, + pub id: common_utils::id_type::GlobalPaymentId, } #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] @@ -139,10 +136,59 @@ pub struct PaymentIntent { pub skip_external_tax_calculation: Option, } +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] +pub struct PaymentLinkConfigRequestForPayments { + /// custom theme for the payment link + pub theme: Option, + /// merchant display logo + pub logo: Option, + /// Custom merchant name for payment link + pub seller_name: Option, + /// Custom layout for sdk + pub sdk_layout: Option, + /// Display only the sdk for payment link + pub display_sdk_only: Option, + /// Enable saved payment method option for payment link + pub enabled_saved_payment_method: Option, + /// Dynamic details related to merchant to be rendered in payment link + pub transaction_details: Option>, +} + +common_utils::impl_to_sql_from_sql_json!(PaymentLinkConfigRequestForPayments); + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] +pub struct PaymentLinkTransactionDetails { + /// Key for the transaction details + pub key: String, + /// Value for the transaction details + pub value: String, + /// UI configuration for the transaction details + pub ui_configuration: Option, +} + +common_utils::impl_to_sql_from_sql_json!(PaymentLinkTransactionDetails); + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] +pub struct TransactionDetailsUiConfiguration { + /// Position of the key-value pair in the UI + pub position: Option, + /// Whether the key should be bold + pub is_key_bold: Option, + /// Whether the value should be bold + pub is_value_bold: Option, +} + +common_utils::impl_to_sql_from_sql_json!(TransactionDetailsUiConfiguration); + #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize, diesel::AsExpression)] #[diesel(sql_type = diesel::sql_types::Jsonb)] pub struct TaxDetails { + /// This is the tax related information that is calculated irrespective of any payment method. + /// This is calculated when the order is created with shipping details pub default: Option, + + /// This is the tax related information that is calculated based on the payment method + /// This is calculated when calling the /calculate_tax API pub payment_method_type: Option, } @@ -168,13 +214,12 @@ pub struct PaymentIntentNew { pub merchant_id: common_utils::id_type::MerchantId, pub status: storage_enums::IntentStatus, pub amount: MinorUnit, - pub currency: Option, + pub currency: storage_enums::Currency, pub amount_captured: Option, pub customer_id: Option, - pub description: Option, - pub return_url: Option, - pub metadata: Option, - pub statement_descriptor_name: Option, + pub description: Option, + pub return_url: Option, + pub metadata: Option, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601")] @@ -182,18 +227,16 @@ pub struct PaymentIntentNew { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub setup_future_usage: Option, - pub off_session: Option, - pub client_secret: Option, + pub client_secret: common_utils::types::ClientSecret, pub active_attempt_id: String, #[diesel(deserialize_as = super::OptionalDieselArray)] pub order_details: Option>, - pub allowed_payment_method_types: Option, - pub connector_metadata: Option, - pub feature_metadata: Option, + pub allowed_payment_method_types: Option, + pub connector_metadata: Option, + pub feature_metadata: Option, pub attempt_count: i16, - pub profile_id: Option, + pub profile_id: common_utils::id_type::ProfileId, pub payment_link_id: Option, - pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, pub request_incremental_authorization: Option, @@ -201,29 +244,25 @@ pub struct PaymentIntentNew { #[serde(with = "common_utils::custom_serde::iso8601::option")] pub session_expiry: Option, pub request_external_three_ds_authentication: Option, - pub charges: Option, pub frm_metadata: Option, pub customer_details: Option, - pub merchant_order_reference_id: Option, - pub is_payment_processor_token_flow: Option, pub shipping_cost: Option, pub organization_id: common_utils::id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, - pub merchant_reference_id: String, + pub merchant_reference_id: Option, pub billing_address: Option, pub shipping_address: Option, pub capture_method: Option, pub authentication_type: Option, - pub amount_to_capture: Option, pub prerouting_algorithm: Option, pub surcharge_amount: Option, pub tax_on_surcharge: Option, - // Denotes the action(approve or reject) taken by merchant in case of manual review. - // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment - pub frm_merchant_decision: Option, - // TODO: change this to global id - pub id: common_utils::id_type::PaymentId, + pub frm_merchant_decision: Option, + pub statement_descriptor: Option, + pub enable_payment_link: Option, + pub apply_mit_exemption: Option, + pub id: common_utils::id_type::GlobalPaymentId, } #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] @@ -305,7 +344,7 @@ pub enum PaymentIntentUpdate { // incremental_authorization_allowed: Option, }, MetadataUpdate { - metadata: serde_json::Value, + metadata: pii::SecretSerdeValue, updated_by: String, }, Update(Box), @@ -464,9 +503,9 @@ pub struct PaymentIntentUpdateFields { pub billing_address: Option, pub return_url: Option, pub description: Option, - pub statement_descriptor_name: Option, + pub statement_descriptor: Option, pub order_details: Option>, - pub metadata: Option, + pub metadata: Option, pub payment_confirm_source: Option, pub updated_by: String, pub session_expiry: Option, @@ -520,16 +559,14 @@ pub struct PaymentIntentUpdateInternal { pub customer_id: Option, pub return_url: Option, pub setup_future_usage: Option, - pub off_session: Option, - pub metadata: Option, + pub metadata: Option, pub modified_at: PrimitiveDateTime, pub active_attempt_id: Option, pub description: Option, - pub statement_descriptor_name: Option, + pub statement_descriptor: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub order_details: Option>, pub attempt_count: Option, - pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, pub authorization_count: Option, @@ -539,8 +576,6 @@ pub struct PaymentIntentUpdateInternal { pub customer_details: Option, pub billing_address: Option, pub shipping_address: Option, - pub merchant_order_reference_id: Option, - pub is_payment_processor_token_flow: Option, pub frm_merchant_decision: Option, } @@ -590,71 +625,71 @@ pub struct PaymentIntentUpdateInternal { #[cfg(all(feature = "v2", feature = "payment_v2"))] impl PaymentIntentUpdate { pub fn apply_changeset(self, source: PaymentIntent) -> PaymentIntent { - let PaymentIntentUpdateInternal { - amount, - currency, - status, - amount_captured, - customer_id, - return_url, - setup_future_usage, - off_session, - metadata, - modified_at: _, - active_attempt_id, - description, - statement_descriptor_name, - order_details, - attempt_count, - frm_merchant_decision, - payment_confirm_source, - updated_by, - surcharge_applicable, - authorization_count, - session_expiry, - request_external_three_ds_authentication, - frm_metadata, - customer_details, - billing_address, - merchant_order_reference_id, - shipping_address, - is_payment_processor_token_flow, - } = self.into(); - PaymentIntent { - amount: amount.unwrap_or(source.amount), - currency: currency.or(source.currency), - status: status.unwrap_or(source.status), - amount_captured: amount_captured.or(source.amount_captured), - customer_id: customer_id.or(source.customer_id), - return_url: return_url.or(source.return_url), - setup_future_usage: setup_future_usage.or(source.setup_future_usage), - off_session: off_session.or(source.off_session), - metadata: metadata.or(source.metadata), - modified_at: common_utils::date_time::now(), - active_attempt_id: active_attempt_id.unwrap_or(source.active_attempt_id), - description: description.or(source.description), - statement_descriptor_name: statement_descriptor_name - .or(source.statement_descriptor_name), - order_details: order_details.or(source.order_details), - attempt_count: attempt_count.unwrap_or(source.attempt_count), - frm_merchant_decision: frm_merchant_decision.or(source.frm_merchant_decision), - payment_confirm_source: payment_confirm_source.or(source.payment_confirm_source), - updated_by, - surcharge_applicable: surcharge_applicable.or(source.surcharge_applicable), - authorization_count: authorization_count.or(source.authorization_count), - session_expiry: session_expiry.or(source.session_expiry), - request_external_three_ds_authentication: request_external_three_ds_authentication - .or(source.request_external_three_ds_authentication), - frm_metadata: frm_metadata.or(source.frm_metadata), - customer_details: customer_details.or(source.customer_details), - billing_address: billing_address.or(source.billing_address), - shipping_address: shipping_address.or(source.shipping_address), - merchant_order_reference_id: merchant_order_reference_id - .or(source.merchant_order_reference_id), - is_payment_processor_token_flow: is_payment_processor_token_flow - .or(source.is_payment_processor_token_flow), - ..source - } + todo!() + // let PaymentIntentUpdateInternal { + // amount, + // currency, + // status, + // amount_captured, + // customer_id, + // return_url, + // setup_future_usage, + // off_session, + // metadata, + // modified_at: _, + // active_attempt_id, + // description, + // statement_descriptor, + // order_details, + // attempt_count, + // frm_merchant_decision, + // payment_confirm_source, + // updated_by, + // surcharge_applicable, + // authorization_count, + // session_expiry, + // request_external_three_ds_authentication, + // frm_metadata, + // customer_details, + // billing_address, + // merchant_order_reference_id, + // shipping_address, + // is_payment_processor_token_flow, + // } = self.into(); + // PaymentIntent { + // amount: amount.unwrap_or(source.amount), + // currency: currency.unwrap_or(source.currency), + // status: status.unwrap_or(source.status), + // amount_captured: amount_captured.or(source.amount_captured), + // customer_id: customer_id.or(source.customer_id), + // return_url: return_url.or(source.return_url), + // setup_future_usage: setup_future_usage.or(source.setup_future_usage), + // off_session: off_session.or(source.off_session), + // metadata: metadata.or(source.metadata), + // modified_at: common_utils::date_time::now(), + // active_attempt_id: active_attempt_id.unwrap_or(source.active_attempt_id), + // description: description.or(source.description), + // statement_descriptor: statement_descriptor.or(source.statement_descriptor), + // order_details: order_details.or(source.order_details), + // attempt_count: attempt_count.unwrap_or(source.attempt_count), + // frm_merchant_decision: frm_merchant_decision.or(source.frm_merchant_decision), + // payment_confirm_source: payment_confirm_source.or(source.payment_confirm_source), + // updated_by, + // surcharge_applicable: surcharge_applicable.or(source.surcharge_applicable), + // authorization_count: authorization_count.or(source.authorization_count), + // session_expiry: session_expiry.or(source.session_expiry), + // request_external_three_ds_authentication: request_external_three_ds_authentication + // .or(source.request_external_three_ds_authentication), + // frm_metadata: frm_metadata.or(source.frm_metadata), + // customer_details: customer_details.or(source.customer_details), + // billing_address: billing_address.or(source.billing_address), + // shipping_address: shipping_address.or(source.shipping_address), + // merchant_order_reference_id: merchant_order_reference_id + // .or(source.merchant_order_reference_id), + // is_payment_processor_token_flow: is_payment_processor_token_flow + // .or(source.is_payment_processor_token_flow), + // ..source + // } } } @@ -751,508 +786,509 @@ impl PaymentIntentUpdate { #[cfg(all(feature = "v2", feature = "payment_v2"))] impl From for PaymentIntentUpdateInternal { fn from(payment_intent_update: PaymentIntentUpdate) -> Self { - match payment_intent_update { - PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - } => Self { - metadata: Some(metadata), - modified_at: common_utils::date_time::now(), - updated_by, - amount: None, - currency: None, - status: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::Update(value) => Self { - amount: Some(value.amount), - currency: Some(value.currency), - setup_future_usage: value.setup_future_usage, - status: Some(value.status), - customer_id: value.customer_id, - shipping_address: value.shipping_address, - billing_address: value.billing_address, - return_url: value.return_url, - description: value.description, - statement_descriptor_name: value.statement_descriptor_name, - order_details: value.order_details, - metadata: value.metadata, - payment_confirm_source: value.payment_confirm_source, - updated_by: value.updated_by, - session_expiry: value.session_expiry, - request_external_three_ds_authentication: value - .request_external_three_ds_authentication, - frm_metadata: value.frm_metadata, - customer_details: value.customer_details, - merchant_order_reference_id: value.merchant_order_reference_id, - amount_captured: None, - off_session: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - attempt_count: None, - frm_merchant_decision: None, - surcharge_applicable: None, - authorization_count: None, - is_payment_processor_token_flow: value.is_payment_processor_token_flow, - }, - PaymentIntentUpdate::PaymentCreateUpdate { - return_url, - status, - customer_id, - shipping_address, - billing_address, - customer_details, - updated_by, - } => Self { - return_url, - status, - customer_id, - customer_details, - modified_at: common_utils::date_time::now(), - updated_by, - amount: None, - currency: None, - amount_captured: None, - setup_future_usage: None, - off_session: None, - metadata: None, - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - billing_address, - merchant_order_reference_id: None, - shipping_address, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::PGStatusUpdate { status, updated_by } => Self { - status: Some(status), - modified_at: common_utils::date_time::now(), - updated_by, - amount: None, - currency: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::MerchantStatusUpdate { - status, - billing_address, - shipping_address, - updated_by, - } => Self { - status: Some(status), - modified_at: common_utils::date_time::now(), - updated_by, - amount: None, - currency: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address, - merchant_order_reference_id: None, - shipping_address, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::ResponseUpdate { - // amount, - // currency, - status, - amount_captured, - // customer_id, - return_url, - updated_by, - } => Self { - // amount, - // currency: Some(currency), - status: Some(status), - amount_captured, - // customer_id, - return_url, - modified_at: common_utils::date_time::now(), - updated_by, - amount: None, - currency: None, - customer_id: None, - setup_future_usage: None, - off_session: None, - metadata: None, - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { - active_attempt_id, - attempt_count, - updated_by, - } => Self { - active_attempt_id: Some(active_attempt_id), - attempt_count: Some(attempt_count), - updated_by, - amount: None, - currency: None, - status: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - description: None, - statement_descriptor_name: None, - order_details: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::StatusAndAttemptUpdate { - status, - active_attempt_id, - attempt_count, - updated_by, - } => Self { - status: Some(status), - active_attempt_id: Some(active_attempt_id), - attempt_count: Some(attempt_count), - updated_by, - amount: None, - currency: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - description: None, - statement_descriptor_name: None, - order_details: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::ApproveUpdate { - status, - frm_merchant_decision, - updated_by, - } => Self { - status: Some(status), - frm_merchant_decision, - updated_by, - amount: None, - currency: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::RejectUpdate { - status, - frm_merchant_decision, - updated_by, - } => Self { - status: Some(status), - frm_merchant_decision, - updated_by, - amount: None, - currency: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::SurchargeApplicableUpdate { - surcharge_applicable, - updated_by, - } => Self { - surcharge_applicable, - updated_by, - amount: None, - currency: None, - status: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { - amount: Some(amount), - currency: None, - status: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - updated_by: String::default(), - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::AuthorizationCountUpdate { - authorization_count, - } => Self { - authorization_count: Some(authorization_count), - amount: None, - currency: None, - status: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - updated_by: String::default(), - surcharge_applicable: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address } => Self { - amount: None, - currency: None, - status: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - updated_by: String::default(), - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address, - is_payment_processor_token_flow: None, - }, - PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { - status, - updated_by, - amount: None, - currency: None, - amount_captured: None, - customer_id: None, - return_url: None, - setup_future_usage: None, - off_session: None, - metadata: None, - modified_at: common_utils::date_time::now(), - active_attempt_id: None, - description: None, - statement_descriptor_name: None, - order_details: None, - attempt_count: None, - frm_merchant_decision: None, - payment_confirm_source: None, - surcharge_applicable: None, - authorization_count: None, - session_expiry: None, - request_external_three_ds_authentication: None, - frm_metadata: None, - customer_details: None, - billing_address: None, - merchant_order_reference_id: None, - shipping_address: None, - is_payment_processor_token_flow: None, - }, - } + todo!() + // match payment_intent_update { + // PaymentIntentUpdate::MetadataUpdate { + // metadata, + // updated_by, + // } => Self { + // metadata: Some(metadata), + // modified_at: common_utils::date_time::now(), + // updated_by, + // amount: None, + // currency: None, + // status: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::Update(value) => Self { + // amount: Some(value.amount), + // currency: Some(value.currency), + // setup_future_usage: value.setup_future_usage, + // status: Some(value.status), + // customer_id: value.customer_id, + // shipping_address: value.shipping_address, + // billing_address: value.billing_address, + // return_url: value.return_url, + // description: value.description, + // statement_descriptor: value.statement_descriptor, + // order_details: value.order_details, + // metadata: value.metadata, + // payment_confirm_source: value.payment_confirm_source, + // updated_by: value.updated_by, + // session_expiry: value.session_expiry, + // request_external_three_ds_authentication: value + // .request_external_three_ds_authentication, + // frm_metadata: value.frm_metadata, + // customer_details: value.customer_details, + // merchant_order_reference_id: value.merchant_order_reference_id, + // amount_captured: None, + // off_session: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // attempt_count: None, + // frm_merchant_decision: None, + // surcharge_applicable: None, + // authorization_count: None, + // is_payment_processor_token_flow: value.is_payment_processor_token_flow, + // }, + // PaymentIntentUpdate::PaymentCreateUpdate { + // return_url, + // status, + // customer_id, + // shipping_address, + // billing_address, + // customer_details, + // updated_by, + // } => Self { + // return_url, + // status, + // customer_id, + // customer_details, + // modified_at: common_utils::date_time::now(), + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // billing_address, + // merchant_order_reference_id: None, + // shipping_address, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::PGStatusUpdate { status, updated_by } => Self { + // status: Some(status), + // modified_at: common_utils::date_time::now(), + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::MerchantStatusUpdate { + // status, + // billing_address, + // shipping_address, + // updated_by, + // } => Self { + // status: Some(status), + // modified_at: common_utils::date_time::now(), + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address, + // merchant_order_reference_id: None, + // shipping_address, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::ResponseUpdate { + // // amount, + // // currency, + // status, + // amount_captured, + // // customer_id, + // return_url, + // updated_by, + // } => Self { + // // amount, + // // currency: Some(currency), + // status: Some(status), + // amount_captured, + // // customer_id, + // return_url, + // modified_at: common_utils::date_time::now(), + // updated_by, + // amount: None, + // currency: None, + // customer_id: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { + // active_attempt_id, + // attempt_count, + // updated_by, + // } => Self { + // active_attempt_id: Some(active_attempt_id), + // attempt_count: Some(attempt_count), + // updated_by, + // amount: None, + // currency: None, + // status: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // description: None, + // statement_descriptor: None, + // order_details: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::StatusAndAttemptUpdate { + // status, + // active_attempt_id, + // attempt_count, + // updated_by, + // } => Self { + // status: Some(status), + // active_attempt_id: Some(active_attempt_id), + // attempt_count: Some(attempt_count), + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // description: None, + // statement_descriptor: None, + // order_details: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::ApproveUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // } => Self { + // status: Some(status), + // frm_merchant_decision, + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::RejectUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // } => Self { + // status: Some(status), + // frm_merchant_decision, + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::SurchargeApplicableUpdate { + // surcharge_applicable, + // updated_by, + // } => Self { + // surcharge_applicable, + // updated_by, + // amount: None, + // currency: None, + // status: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { + // amount: Some(amount), + // currency: None, + // status: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // updated_by: String::default(), + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::AuthorizationCountUpdate { + // authorization_count, + // } => Self { + // authorization_count: Some(authorization_count), + // amount: None, + // currency: None, + // status: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // updated_by: String::default(), + // surcharge_applicable: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address } => Self { + // amount: None, + // currency: None, + // status: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // updated_by: String::default(), + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address, + // is_payment_processor_token_flow: None, + // }, + // PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { + // status, + // updated_by, + // amount: None, + // currency: None, + // amount_captured: None, + // customer_id: None, + // return_url: None, + // setup_future_usage: None, + // off_session: None, + // metadata: None, + // modified_at: common_utils::date_time::now(), + // active_attempt_id: None, + // description: None, + // statement_descriptor: None, + // order_details: None, + // attempt_count: None, + // frm_merchant_decision: None, + // payment_confirm_source: None, + // surcharge_applicable: None, + // authorization_count: None, + // session_expiry: None, + // request_external_three_ds_authentication: None, + // frm_metadata: None, + // customer_details: None, + // billing_address: None, + // merchant_order_reference_id: None, + // shipping_address: None, + // is_payment_processor_token_flow: None, + // }, + // } } } diff --git a/crates/diesel_models/src/query/payment_intent.rs b/crates/diesel_models/src/query/payment_intent.rs index ccf82d28048..e148c76686e 100644 --- a/crates/diesel_models/src/query/payment_intent.rs +++ b/crates/diesel_models/src/query/payment_intent.rs @@ -44,7 +44,7 @@ impl PaymentIntent { #[cfg(all(feature = "v2", feature = "payment_v2"))] pub async fn find_by_global_id( conn: &PgPooledConn, - id: &common_utils::id_type::PaymentId, + id: &common_utils::id_type::GlobalPaymentId, ) -> StorageResult { generics::generic_find_by_id::<::Table, _, _>(conn, id.to_owned()).await } @@ -105,7 +105,7 @@ impl PaymentIntent { } #[cfg(all(feature = "v2", feature = "payment_v2"))] - pub async fn find_optional_by_merchant_id_merchant_reference_id( + pub async fn find_optional_by_merchant_reference_id_merchant_id( conn: &PgPooledConn, merchant_reference_id: &str, merchant_id: &common_utils::id_type::MerchantId, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index ca62d2c477e..a66286e02db 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -836,7 +836,7 @@ diesel::table! { merchant_id -> Varchar, status -> IntentStatus, amount -> Int8, - currency -> Nullable, + currency -> Currency, amount_captured -> Nullable, #[max_length = 64] customer_id -> Nullable, @@ -845,15 +845,12 @@ diesel::table! { #[max_length = 255] return_url -> Nullable, metadata -> Nullable, - #[max_length = 255] - statement_descriptor_name -> Nullable, created_at -> Timestamp, modified_at -> Timestamp, last_synced -> Nullable, setup_future_usage -> Nullable, - off_session -> Nullable, #[max_length = 128] - client_secret -> Nullable, + client_secret -> Varchar, #[max_length = 64] active_attempt_id -> Varchar, order_details -> Nullable>>, @@ -862,10 +859,9 @@ diesel::table! { feature_metadata -> Nullable, attempt_count -> Int2, #[max_length = 64] - profile_id -> Nullable, + profile_id -> Varchar, #[max_length = 255] payment_link_id -> Nullable, - payment_confirm_source -> Nullable, #[max_length = 32] updated_by -> Varchar, surcharge_applicable -> Nullable, @@ -873,29 +869,32 @@ diesel::table! { authorization_count -> Nullable, session_expiry -> Nullable, request_external_three_ds_authentication -> Nullable, - charges -> Nullable, frm_metadata -> Nullable, customer_details -> Nullable, - #[max_length = 255] - merchant_order_reference_id -> Nullable, - is_payment_processor_token_flow -> Nullable, shipping_cost -> Nullable, #[max_length = 32] organization_id -> Varchar, tax_details -> Nullable, skip_external_tax_calculation -> Nullable, #[max_length = 64] - merchant_reference_id -> Varchar, + merchant_reference_id -> Nullable, billing_address -> Nullable, shipping_address -> Nullable, capture_method -> Nullable, authentication_type -> Nullable, - amount_to_capture -> Nullable, prerouting_algorithm -> Nullable, surcharge_amount -> Nullable, tax_on_surcharge -> Nullable, #[max_length = 64] frm_merchant_decision -> Nullable, + #[max_length = 255] + statement_descriptor -> Nullable, + enable_payment_link -> Nullable, + apply_mit_exemption -> Nullable, + customer_present -> Nullable, + #[max_length = 64] + routing_algorithm_id -> Nullable, + payment_link_config -> Nullable, #[max_length = 64] id -> Varchar, } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index fee2155e8dc..da56a1dc304 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -82,70 +82,180 @@ impl PaymentIntent { } #[cfg(all(feature = "v2", feature = "payment_v2",))] - pub fn get_id(&self) -> &id_type::PaymentId { + pub fn get_id(&self) -> &id_type::GlobalPaymentId { &self.id } } +#[cfg(feature = "v2")] +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub enum TaxCalculationOverride { + /// Skip calling the external tax provider + Skip, + /// Calculate tax by calling the external tax provider + Calculate, +} + +#[cfg(feature = "v2")] +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub enum SurchargeCalculationOverride { + /// Skip calculating surcharge + Skip, + /// Calculate surcharge + Calculate, +} + +#[cfg(feature = "v2")] +impl From> for TaxCalculationOverride { + fn from(value: Option) -> Self { + match value { + Some(true) => TaxCalculationOverride::Calculate, + _ => TaxCalculationOverride::Skip, + } + } +} + +#[cfg(feature = "v2")] +impl From> for SurchargeCalculationOverride { + fn from(value: Option) -> Self { + match value { + Some(true) => SurchargeCalculationOverride::Calculate, + _ => SurchargeCalculationOverride::Skip, + } + } +} + +#[cfg(feature = "v2")] +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub struct AmountDetails { + /// The amount of the order in the lowest denomination of currency + order_amount: MinorUnit, + /// The currency of the order + currency: common_enums::Currency, + /// The shipping cost of the order. This has to be collected from the merchant + shipping_cost: Option, + /// Tax details related to the order. This will be calculated by the external tax provider + tax_details: Option, + /// The action to whether calculate tax by calling external tax provider or not + skip_external_tax_calculation: TaxCalculationOverride, + /// The action to whether calculate surcharge or not + skip_surcharge_calculation: SurchargeCalculationOverride, + /// The surcharge amount to be added to the order, collected from the merchant + surcharge_amount: Option, + /// tax on surcharge amount + tax_on_surcharge: Option, +} + +#[cfg(feature = "v2")] +impl AmountDetails { + /// Get the action to whether calculate surcharge or not as a boolean value + fn get_surcharge_action_as_bool(&self) -> bool { + match self.skip_surcharge_calculation { + SurchargeCalculationOverride::Skip => false, + SurchargeCalculationOverride::Calculate => true, + } + } + + /// Get the action to whether calculate external tax or not as a boolean value + fn get_external_tax_action_as_bool(&self) -> bool { + match self.skip_external_tax_calculation { + TaxCalculationOverride::Skip => false, + TaxCalculationOverride::Calculate => true, + } + } +} + #[cfg(all(feature = "v2", feature = "payment_v2"))] #[derive(Clone, Debug, PartialEq, serde::Serialize)] pub struct PaymentIntent { + /// The global identifier for the payment intent. This is generated by the system. + /// The format of the global id is `{cell_id:5}_pay_{time_ordered_uuid:32}`. + pub id: id_type::GlobalPaymentId, + /// The identifier for the merchant. This is automatically derived from the api key used to create the payment. pub merchant_id: id_type::MerchantId, + /// The status of payment intent. pub status: storage_enums::IntentStatus, - pub amount: MinorUnit, - pub currency: Option, + /// The amount related details of the payment + pub amount_details: AmountDetails, + /// The total amount captured for the order. This is the sum of all the captured amounts for the order. pub amount_captured: Option, + /// The identifier for the customer. This is the identifier for the customer in the merchant's system. pub customer_id: Option, - pub description: Option, - pub return_url: Option, - pub metadata: Option, - pub statement_descriptor_name: Option, + /// The description of the order. This will be passed to connectors which support description. + pub description: Option, + /// The return url for the payment. This is the url to which the user will be redirected after the payment is completed. + pub return_url: Option, + /// The metadata for the payment intent. This is the metadata that will be passed to the connectors. + pub metadata: Option, + /// The statement descriptor for the order, this will be displayed in the user's bank statement. + pub statement_descriptor: Option, + /// The time at which the order was created #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, + /// The time at which the order was last modified #[serde(with = "common_utils::custom_serde::iso8601")] pub modified_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub setup_future_usage: Option, - pub off_session: Option, - pub client_secret: Option, + /// The client secret that is generated for the payment. This is used to authenticate the payment from client facing apis. + pub client_secret: common_utils::types::ClientSecret, + /// The active attempt for the payment intent. This is the payment attempt that is currently active for the payment intent. pub active_attempt: RemoteStorageObject, + /// The order details for the payment. pub order_details: Option>, - pub allowed_payment_method_types: Option, - pub connector_metadata: Option, - pub feature_metadata: Option, + /// This is the list of payment method types that are allowed for the payment intent. + /// This field allows the merchant to restrict the payment methods that can be used for the payment intent. + pub allowed_payment_method_types: Option, + /// This metadata contains details about + pub connector_metadata: Option, + pub feature_metadata: Option, + /// Number of attempts that have been made for the order pub attempt_count: i16, - pub profile_id: Option, + /// The profile id for the payment. + pub profile_id: id_type::ProfileId, + /// The payment link id for the payment. This is generated only if `enable_payment_link` is set to true. pub payment_link_id: Option, - // Denotes the action(approve or reject) taken by merchant in case of manual review. - // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment - pub frm_merchant_decision: Option, - pub payment_confirm_source: Option, - + /// This Denotes the action(approve or reject) taken by merchant in case of manual review. + /// Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment + pub frm_merchant_decision: Option, + /// Denotes the last instance which updated the payment pub updated_by: String, - pub surcharge_applicable: Option, + /// Denotes whether merchant requested for incremental authorization to be enabled for this payment. pub request_incremental_authorization: Option, + /// Denotes the number of authorizations that have been made for the payment. pub authorization_count: Option, + /// Denotes the client secret expiry for the payment. This is the time at which the client secret will expire. #[serde(with = "common_utils::custom_serde::iso8601::option")] pub session_expiry: Option, - pub request_external_three_ds_authentication: Option, - pub charges: Option, + /// Denotes whether merchant requested for 3ds authentication to be enabled for this payment. + pub request_external_three_ds_authentication: common_enums::External3dsAuthenticationRequest, + /// Metadata related to fraud and risk management pub frm_metadata: Option, + /// The details of the customer in a denormalized form. Only a subset of fields are stored. pub customer_details: Option>>, - pub merchant_order_reference_id: Option, - pub is_payment_processor_token_flow: Option, - pub shipping_cost: Option, - pub tax_details: Option, - pub merchant_reference_id: String, + /// The reference id for the order in the merchant's system. This value can be passed by the merchant. + pub merchant_reference_id: Option, + /// The billing address for the order in a denormalized form. pub billing_address: Option>>, + /// The shipping address for the order in a denormalized form. pub shipping_address: Option>>, + /// Capture method for the payment pub capture_method: Option, - pub id: id_type::PaymentId, + /// Authentication type that is requested by the merchant for this payment. pub authentication_type: Option, - pub amount_to_capture: Option, + /// This contains the pre routing results that are done when routing is done during listing the payment methods. pub prerouting_algorithm: Option, - pub surcharge_amount: Option, - pub tax_on_surcharge: Option, + /// The organization id for the payment. This is derived from the merchant account pub organization_id: id_type::OrganizationId, - pub skip_external_tax_calculation: Option, + /// Denotes the request by the merchant whether to enable a payment link for this payment. + pub enable_payment_link: common_enums::EnablePaymentLinkRequest, + /// Denotes the request by the merchant whether to apply MIT exemption for this payment + pub apply_mit_exemption: common_enums::MitExemptionRequest, + /// Denotes whether the customer is present during the payment flow. This information may be used for 3ds authentication + pub customer_present: common_enums::PresenceOfCustomerDuringPayment, + /// Denotes the override for payment link configuration + pub payment_link_config: Option, + /// The straight through routing algorithm id that is used for this payment. This overrides the default routing algorithm that is configured in business profile. + pub routing_algorithm_id: Option, } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 619ebcce68a..eb207a19508 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -60,7 +60,7 @@ pub trait PaymentIntentInterface { async fn find_payment_intent_by_id( &self, state: &KeyManagerState, - id: &id_type::PaymentId, + id: &id_type::GlobalPaymentId, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; @@ -146,120 +146,11 @@ pub struct CustomerData { pub phone_country_code: Option, } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] -#[derive(Clone, Debug, PartialEq)] -pub struct PaymentIntentNew { - pub payment_id: id_type::PaymentId, - pub merchant_id: id_type::MerchantId, - pub status: storage_enums::IntentStatus, - pub amount: MinorUnit, - pub currency: Option, - pub amount_captured: Option, - pub customer_id: Option, - pub description: Option, - pub return_url: Option, - pub metadata: Option, - pub frm_metadata: Option, - pub connector_id: Option, - pub shipping_address_id: Option, - pub billing_address_id: Option, - pub statement_descriptor_name: Option, - pub statement_descriptor_suffix: Option, - pub created_at: Option, - pub modified_at: Option, - pub last_synced: Option, - pub setup_future_usage: Option, - pub off_session: Option, - pub client_secret: Option, - pub active_attempt: RemoteStorageObject, - pub business_country: Option, - pub business_label: Option, - pub order_details: Option>, - pub allowed_payment_method_types: Option, - pub connector_metadata: Option, - pub feature_metadata: Option, - pub attempt_count: i16, - pub profile_id: Option, - pub merchant_decision: Option, - pub payment_link_id: Option, - pub payment_confirm_source: Option, - - pub updated_by: String, - pub surcharge_applicable: Option, - pub request_incremental_authorization: Option, - pub incremental_authorization_allowed: Option, - pub authorization_count: Option, - pub fingerprint_id: Option, - pub session_expiry: Option, - pub request_external_three_ds_authentication: Option, - pub charges: Option, - pub customer_details: Option>>, - pub billing_details: Option>>, - pub shipping_details: Option>>, - pub is_payment_processor_token_flow: Option, - pub organization_id: id_type::OrganizationId, - pub skip_external_tax_calculation: Option, -} - -#[cfg(all(feature = "v2", feature = "payment_v2"))] -#[derive(Clone, Debug, PartialEq)] -pub struct PaymentIntentNew { - pub payment_id: id_type::PaymentId, - pub merchant_id: id_type::MerchantId, - pub status: storage_enums::IntentStatus, - pub amount: MinorUnit, - pub currency: Option, - pub amount_captured: Option, - pub customer_id: Option, - pub description: Option, - pub return_url: Option, - pub metadata: Option, - pub frm_metadata: Option, - pub connector_id: Option, - pub shipping_address_id: Option, - pub billing_address_id: Option, - pub statement_descriptor_name: Option, - pub statement_descriptor_suffix: Option, - pub created_at: Option, - pub modified_at: Option, - pub last_synced: Option, - pub setup_future_usage: Option, - pub off_session: Option, - pub client_secret: Option, - pub active_attempt: RemoteStorageObject, - pub business_country: Option, - pub business_label: Option, - pub order_details: Option>, - pub allowed_payment_method_types: Option, - pub connector_metadata: Option, - pub feature_metadata: Option, - pub attempt_count: i16, - pub profile_id: Option, - pub merchant_decision: Option, - pub payment_link_id: Option, - pub payment_confirm_source: Option, - - pub updated_by: String, - pub surcharge_applicable: Option, - pub request_incremental_authorization: Option, - pub incremental_authorization_allowed: Option, - pub authorization_count: Option, - pub fingerprint_id: Option, - pub session_expiry: Option, - pub request_external_three_ds_authentication: Option, - pub charges: Option, - pub customer_details: Option>>, - pub billing_details: Option>>, - pub shipping_details: Option>>, - pub is_payment_processor_token_flow: Option, - pub organization_id: id_type::OrganizationId, -} - #[cfg(all(feature = "v2", feature = "payment_v2"))] #[derive(Debug, Clone, Serialize)] pub struct PaymentIntentUpdateFields { - pub amount: MinorUnit, - pub currency: storage_enums::Currency, + pub amount: Option, + pub currency: Option, pub setup_future_usage: Option, pub status: storage_enums::IntentStatus, pub customer_id: Option, @@ -267,9 +158,9 @@ pub struct PaymentIntentUpdateFields { pub billing_address: Option>>, pub return_url: Option, pub description: Option, - pub statement_descriptor_name: Option, + pub statement_descriptor: Option, pub order_details: Option>, - pub metadata: Option, + pub metadata: Option, pub payment_confirm_source: Option, pub updated_by: String, pub session_expiry: Option, @@ -394,6 +285,7 @@ pub enum PaymentIntentUpdate { }, } +// TODO: remove all enum variants and create new variants that should be used for v2 #[cfg(all(feature = "v2", feature = "payment_v2"))] #[derive(Debug, Clone, Serialize)] pub enum PaymentIntentUpdate { @@ -404,7 +296,7 @@ pub enum PaymentIntentUpdate { updated_by: String, }, MetadataUpdate { - metadata: serde_json::Value, + metadata: pii::SecretSerdeValue, updated_by: String, }, Update(Box), @@ -478,14 +370,14 @@ pub struct PaymentIntentUpdateInternal { pub return_url: Option, pub setup_future_usage: Option, pub off_session: Option, - pub metadata: Option, + pub metadata: Option, pub modified_at: Option, pub active_attempt_id: Option, pub description: Option, - pub statement_descriptor_name: Option, + pub statement_descriptor: Option, pub order_details: Option>, pub attempt_count: Option, - pub frm_merchant_decision: Option, + pub frm_merchant_decision: Option, pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, @@ -547,177 +439,178 @@ pub struct PaymentIntentUpdateInternal { #[cfg(all(feature = "v2", feature = "payment_v2"))] impl From for PaymentIntentUpdateInternal { fn from(payment_intent_update: PaymentIntentUpdate) -> Self { - match payment_intent_update { - PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - } => Self { - metadata: Some(metadata), - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::Update(value) => Self { - amount: Some(value.amount), - currency: Some(value.currency), - setup_future_usage: value.setup_future_usage, - status: Some(value.status), - customer_id: value.customer_id, - return_url: value.return_url, - description: value.description, - statement_descriptor_name: value.statement_descriptor_name, - order_details: value.order_details, - metadata: value.metadata, - payment_confirm_source: value.payment_confirm_source, - updated_by: value.updated_by, - session_expiry: value.session_expiry, - request_external_three_ds_authentication: value - .request_external_three_ds_authentication, - frm_metadata: value.frm_metadata, - customer_details: value.customer_details, - billing_address: value.billing_address, - merchant_order_reference_id: value.merchant_order_reference_id, - shipping_address: value.shipping_address, - is_payment_processor_token_flow: value.is_payment_processor_token_flow, - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::PaymentCreateUpdate { - return_url, - status, - customer_id, - shipping_address, - billing_address, - customer_details, - updated_by, - } => Self { - return_url, - status, - customer_id, - shipping_address, - billing_address, - customer_details, - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::PGStatusUpdate { status, updated_by } => Self { - status: Some(status), - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::MerchantStatusUpdate { - status, - shipping_address, - billing_address, - updated_by, - } => Self { - status: Some(status), - shipping_address, - billing_address, - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::ResponseUpdate { - // amount, - // currency, - status, - amount_captured, - // customer_id, - return_url, - updated_by, - } => Self { - // amount, - // currency: Some(currency), - status: Some(status), - amount_captured, - // customer_id, - return_url, - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { - active_attempt_id, - attempt_count, - updated_by, - } => Self { - active_attempt_id: Some(active_attempt_id), - attempt_count: Some(attempt_count), - updated_by, - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::StatusAndAttemptUpdate { - status, - active_attempt_id, - attempt_count, - updated_by, - } => Self { - status: Some(status), - active_attempt_id: Some(active_attempt_id), - attempt_count: Some(attempt_count), - updated_by, - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::ApproveUpdate { - status, - frm_merchant_decision, - updated_by, - } => Self { - status: Some(status), - frm_merchant_decision, - updated_by, - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::RejectUpdate { - status, - frm_merchant_decision, - updated_by, - } => Self { - status: Some(status), - frm_merchant_decision, - updated_by, - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::SurchargeApplicableUpdate { - surcharge_applicable, - updated_by, - } => Self { - surcharge_applicable: Some(surcharge_applicable), - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { - amount: Some(amount), - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::AuthorizationCountUpdate { - authorization_count, - } => Self { - authorization_count: Some(authorization_count), - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address } => Self { - shipping_address, - modified_at: Some(common_utils::date_time::now()), - ..Default::default() - }, - PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { - status, - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - } + todo!() + // match payment_intent_update { + // PaymentIntentUpdate::MetadataUpdate { + // metadata, + // updated_by, + // } => Self { + // metadata: Some(metadata), + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // PaymentIntentUpdate::Update(value) => Self { + // amount: Some(value.amount), + // currency: Some(value.currency), + // setup_future_usage: value.setup_future_usage, + // status: Some(value.status), + // customer_id: value.customer_id, + // return_url: value.return_url, + // description: value.description, + // statement_descriptor: value.statement_descriptor, + // order_details: value.order_details, + // metadata: value.metadata, + // payment_confirm_source: value.payment_confirm_source, + // updated_by: value.updated_by, + // session_expiry: value.session_expiry, + // request_external_three_ds_authentication: value + // .request_external_three_ds_authentication, + // frm_metadata: value.frm_metadata, + // customer_details: value.customer_details, + // billing_address: value.billing_address, + // merchant_order_reference_id: value.merchant_order_reference_id, + // shipping_address: value.shipping_address, + // is_payment_processor_token_flow: value.is_payment_processor_token_flow, + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::PaymentCreateUpdate { + // return_url, + // status, + // customer_id, + // shipping_address, + // billing_address, + // customer_details, + // updated_by, + // } => Self { + // return_url, + // status, + // customer_id, + // shipping_address, + // billing_address, + // customer_details, + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // PaymentIntentUpdate::PGStatusUpdate { status, updated_by } => Self { + // status: Some(status), + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // PaymentIntentUpdate::MerchantStatusUpdate { + // status, + // shipping_address, + // billing_address, + // updated_by, + // } => Self { + // status: Some(status), + // shipping_address, + // billing_address, + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // PaymentIntentUpdate::ResponseUpdate { + // // amount, + // // currency, + // status, + // amount_captured, + // // customer_id, + // return_url, + // updated_by, + // } => Self { + // // amount, + // // currency: Some(currency), + // status: Some(status), + // amount_captured, + // // customer_id, + // return_url, + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { + // active_attempt_id, + // attempt_count, + // updated_by, + // } => Self { + // active_attempt_id: Some(active_attempt_id), + // attempt_count: Some(attempt_count), + // updated_by, + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::StatusAndAttemptUpdate { + // status, + // active_attempt_id, + // attempt_count, + // updated_by, + // } => Self { + // status: Some(status), + // active_attempt_id: Some(active_attempt_id), + // attempt_count: Some(attempt_count), + // updated_by, + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::ApproveUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // } => Self { + // status: Some(status), + // frm_merchant_decision, + // updated_by, + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::RejectUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // } => Self { + // status: Some(status), + // frm_merchant_decision, + // updated_by, + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::SurchargeApplicableUpdate { + // surcharge_applicable, + // updated_by, + // } => Self { + // surcharge_applicable: Some(surcharge_applicable), + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { + // amount: Some(amount), + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::AuthorizationCountUpdate { + // authorization_count, + // } => Self { + // authorization_count: Some(authorization_count), + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address } => Self { + // shipping_address, + // modified_at: Some(common_utils::date_time::now()), + // ..Default::default() + // }, + // PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { + // status, + // modified_at: Some(common_utils::date_time::now()), + // updated_by, + // ..Default::default() + // }, + // } } } @@ -856,215 +749,216 @@ impl From for PaymentIntentUpdateInternal { status, merchant_decision, updated_by, - } => Self { - status: Some(status), - merchant_decision, - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::RejectUpdate { - status, - merchant_decision, - updated_by, - } => Self { - status: Some(status), - merchant_decision, - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::SurchargeApplicableUpdate { - surcharge_applicable, - updated_by, - } => Self { - surcharge_applicable: Some(surcharge_applicable), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { - amount: Some(amount), - ..Default::default() - }, - PaymentIntentUpdate::AuthorizationCountUpdate { - authorization_count, - } => Self { - authorization_count: Some(authorization_count), - ..Default::default() - }, - PaymentIntentUpdate::CompleteAuthorizeUpdate { - shipping_address_id, - } => Self { - shipping_address_id, - ..Default::default() - }, - PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { - status, - modified_at: Some(common_utils::date_time::now()), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::SessionResponseUpdate { - tax_details, - shipping_address_id, - updated_by, - shipping_details, - } => Self { - tax_details: Some(tax_details), - shipping_address_id, - updated_by, - shipping_details, - ..Default::default() - }, - } - } -} - -use diesel_models::{ - PaymentIntentUpdate as DieselPaymentIntentUpdate, - PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields, -}; -#[cfg(all(feature = "v2", feature = "payment_v2"))] -impl From for DieselPaymentIntentUpdate { - fn from(value: PaymentIntentUpdate) -> Self { - match value { - PaymentIntentUpdate::ResponseUpdate { - status, - amount_captured, - return_url, - updated_by, - } => Self::ResponseUpdate { - status, - amount_captured, - return_url, - updated_by, - }, - PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - } => Self::MetadataUpdate { - metadata, - updated_by, - }, - PaymentIntentUpdate::Update(value) => { - Self::Update(Box::new(DieselPaymentIntentUpdateFields { - amount: value.amount, - currency: value.currency, - setup_future_usage: value.setup_future_usage, - status: value.status, - customer_id: value.customer_id, - return_url: value.return_url, - description: value.description, - statement_descriptor_name: value.statement_descriptor_name, - order_details: value.order_details, - metadata: value.metadata, - payment_confirm_source: value.payment_confirm_source, - updated_by: value.updated_by, - session_expiry: value.session_expiry, - request_external_three_ds_authentication: value - .request_external_three_ds_authentication, - frm_metadata: value.frm_metadata, - customer_details: value.customer_details.map(Encryption::from), - billing_address: value.billing_address.map(Encryption::from), - shipping_address: value.shipping_address.map(Encryption::from), - merchant_order_reference_id: value.merchant_order_reference_id, - is_payment_processor_token_flow: value.is_payment_processor_token_flow, - })) - } - PaymentIntentUpdate::PaymentCreateUpdate { - return_url, - status, - customer_id, - shipping_address, - billing_address, - customer_details, - updated_by, - } => Self::PaymentCreateUpdate { - return_url, - status, - customer_id, - shipping_address: shipping_address.map(Encryption::from), - billing_address: billing_address.map(Encryption::from), - customer_details: customer_details.map(Encryption::from), - updated_by, - }, - PaymentIntentUpdate::MerchantStatusUpdate { - status, - shipping_address, - billing_address, - updated_by, - } => Self::MerchantStatusUpdate { - status, - shipping_address: shipping_address.map(Encryption::from), - billing_address: billing_address.map(Encryption::from), - updated_by, - }, - PaymentIntentUpdate::PGStatusUpdate { status, updated_by } => { - Self::PGStatusUpdate { status, updated_by } - } - PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { - active_attempt_id, - attempt_count, - updated_by, - } => Self::PaymentAttemptAndAttemptCountUpdate { - active_attempt_id, - attempt_count, - updated_by, - }, - PaymentIntentUpdate::StatusAndAttemptUpdate { - status, - active_attempt_id, - attempt_count, - updated_by, - } => Self::StatusAndAttemptUpdate { - status, - active_attempt_id, - attempt_count, - updated_by, - }, - PaymentIntentUpdate::ApproveUpdate { - status, - frm_merchant_decision, - updated_by, - } => Self::ApproveUpdate { - status, - frm_merchant_decision, + } => Self { + status: Some(status), + merchant_decision, updated_by, + ..Default::default() }, PaymentIntentUpdate::RejectUpdate { status, - frm_merchant_decision, + merchant_decision, updated_by, - } => Self::RejectUpdate { - status, - frm_merchant_decision, + } => Self { + status: Some(status), + merchant_decision, updated_by, + ..Default::default() }, PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable, updated_by, - } => Self::SurchargeApplicableUpdate { + } => Self { surcharge_applicable: Some(surcharge_applicable), updated_by, + ..Default::default() + }, + PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { + amount: Some(amount), + ..Default::default() }, - PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => { - Self::IncrementalAuthorizationAmountUpdate { amount } - } PaymentIntentUpdate::AuthorizationCountUpdate { authorization_count, - } => Self::AuthorizationCountUpdate { - authorization_count, + } => Self { + authorization_count: Some(authorization_count), + ..Default::default() + }, + PaymentIntentUpdate::CompleteAuthorizeUpdate { + shipping_address_id, + } => Self { + shipping_address_id, + ..Default::default() + }, + PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { + status, + modified_at: Some(common_utils::date_time::now()), + updated_by, + ..Default::default() + }, + PaymentIntentUpdate::SessionResponseUpdate { + tax_details, + shipping_address_id, + updated_by, + shipping_details, + } => Self { + tax_details: Some(tax_details), + shipping_address_id, + updated_by, + shipping_details, + ..Default::default() }, - PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address } => { - Self::CompleteAuthorizeUpdate { - shipping_address: shipping_address.map(Encryption::from), - } - } - PaymentIntentUpdate::ManualUpdate { status, updated_by } => { - Self::ManualUpdate { status, updated_by } - } } } } +use diesel_models::{ + PaymentIntentUpdate as DieselPaymentIntentUpdate, + PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields, +}; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +impl From for DieselPaymentIntentUpdate { + fn from(value: PaymentIntentUpdate) -> Self { + todo!() + // match value { + // PaymentIntentUpdate::ResponseUpdate { + // status, + // amount_captured, + // return_url, + // updated_by, + // } => Self::ResponseUpdate { + // status, + // amount_captured, + // return_url, + // updated_by, + // }, + // PaymentIntentUpdate::MetadataUpdate { + // metadata, + // updated_by, + // } => Self::MetadataUpdate { + // metadata, + // updated_by, + // }, + // PaymentIntentUpdate::Update(value) => { + // Self::Update(Box::new(DieselPaymentIntentUpdateFields { + // amount: value.amount, + // currency: value.currency, + // setup_future_usage: value.setup_future_usage, + // status: value.status, + // customer_id: value.customer_id, + // return_url: value.return_url, + // description: value.description, + // statement_descriptor: value.statement_descriptor, + // order_details: value.order_details, + // metadata: value.metadata, + // payment_confirm_source: value.payment_confirm_source, + // updated_by: value.updated_by, + // session_expiry: value.session_expiry, + // request_external_three_ds_authentication: value + // .request_external_three_ds_authentication, + // frm_metadata: value.frm_metadata, + // customer_details: value.customer_details.map(Encryption::from), + // billing_address: value.billing_address.map(Encryption::from), + // shipping_address: value.shipping_address.map(Encryption::from), + // merchant_order_reference_id: value.merchant_order_reference_id, + // is_payment_processor_token_flow: value.is_payment_processor_token_flow, + // })) + // } + // PaymentIntentUpdate::PaymentCreateUpdate { + // return_url, + // status, + // customer_id, + // shipping_address, + // billing_address, + // customer_details, + // updated_by, + // } => Self::PaymentCreateUpdate { + // return_url, + // status, + // customer_id, + // shipping_address: shipping_address.map(Encryption::from), + // billing_address: billing_address.map(Encryption::from), + // customer_details: customer_details.map(Encryption::from), + // updated_by, + // }, + // PaymentIntentUpdate::MerchantStatusUpdate { + // status, + // shipping_address, + // billing_address, + // updated_by, + // } => Self::MerchantStatusUpdate { + // status, + // shipping_address: shipping_address.map(Encryption::from), + // billing_address: billing_address.map(Encryption::from), + // updated_by, + // }, + // PaymentIntentUpdate::PGStatusUpdate { status, updated_by } => { + // Self::PGStatusUpdate { status, updated_by } + // } + // PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { + // active_attempt_id, + // attempt_count, + // updated_by, + // } => Self::PaymentAttemptAndAttemptCountUpdate { + // active_attempt_id, + // attempt_count, + // updated_by, + // }, + // PaymentIntentUpdate::StatusAndAttemptUpdate { + // status, + // active_attempt_id, + // attempt_count, + // updated_by, + // } => Self::StatusAndAttemptUpdate { + // status, + // active_attempt_id, + // attempt_count, + // updated_by, + // }, + // PaymentIntentUpdate::ApproveUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // } => Self::ApproveUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // }, + // PaymentIntentUpdate::RejectUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // } => Self::RejectUpdate { + // status, + // frm_merchant_decision, + // updated_by, + // }, + // PaymentIntentUpdate::SurchargeApplicableUpdate { + // surcharge_applicable, + // updated_by, + // } => Self::SurchargeApplicableUpdate { + // surcharge_applicable: Some(surcharge_applicable), + // updated_by, + // }, + // PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => { + // Self::IncrementalAuthorizationAmountUpdate { amount } + // } + // PaymentIntentUpdate::AuthorizationCountUpdate { + // authorization_count, + // } => Self::AuthorizationCountUpdate { + // authorization_count, + // }, + // PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address } => { + // Self::CompleteAuthorizeUpdate { + // shipping_address: shipping_address.map(Encryption::from), + // } + // } + // PaymentIntentUpdate::ManualUpdate { status, updated_by } => { + // Self::ManualUpdate { status, updated_by } + // } + // } + } +} + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] impl From for DieselPaymentIntentUpdate { fn from(value: PaymentIntentUpdate) -> Self { @@ -1236,70 +1130,72 @@ impl From for DieselPaymentIntentUpdate { } } +// TODO: evaluate if we will be using the same update struct for v2 as well, uncomment this and make necessary changes if necessary #[cfg(all(feature = "v2", feature = "payment_v2"))] impl From for diesel_models::PaymentIntentUpdateInternal { fn from(value: PaymentIntentUpdateInternal) -> Self { - let modified_at = common_utils::date_time::now(); - let PaymentIntentUpdateInternal { - amount, - currency, - status, - amount_captured, - customer_id, - return_url, - setup_future_usage, - off_session, - metadata, - modified_at: _, - active_attempt_id, - description, - statement_descriptor_name, - order_details, - attempt_count, - frm_merchant_decision, - payment_confirm_source, - updated_by, - surcharge_applicable, - authorization_count, - session_expiry, - request_external_three_ds_authentication, - frm_metadata, - customer_details, - billing_address, - merchant_order_reference_id, - shipping_address, - is_payment_processor_token_flow, - } = value; - Self { - amount, - currency, - status, - amount_captured, - customer_id, - return_url, - setup_future_usage, - off_session, - metadata, - modified_at, - active_attempt_id, - description, - statement_descriptor_name, - order_details, - attempt_count, - frm_merchant_decision, - payment_confirm_source, - updated_by, - surcharge_applicable, - authorization_count, - session_expiry, - request_external_three_ds_authentication, - frm_metadata, - customer_details: customer_details.map(Encryption::from), - billing_address: billing_address.map(Encryption::from), - merchant_order_reference_id, - shipping_address: shipping_address.map(Encryption::from), - is_payment_processor_token_flow, - } + todo!() + // let modified_at = common_utils::date_time::now(); + // let PaymentIntentUpdateInternal { + // amount, + // currency, + // status, + // amount_captured, + // customer_id, + // return_url, + // setup_future_usage, + // off_session, + // metadata, + // modified_at: _, + // active_attempt_id, + // description, + // statement_descriptor, + // order_details, + // attempt_count, + // frm_merchant_decision, + // payment_confirm_source, + // updated_by, + // surcharge_applicable, + // authorization_count, + // session_expiry, + // request_external_three_ds_authentication, + // frm_metadata, + // customer_details, + // billing_address, + // merchant_order_reference_id, + // shipping_address, + // is_payment_processor_token_flow, + // } = value; + // Self { + // amount, + // currency, + // status, + // amount_captured, + // customer_id, + // return_url, + // setup_future_usage, + // off_session, + // metadata, + // modified_at, + // active_attempt_id, + // description, + // statement_descriptor, + // order_details, + // attempt_count, + // frm_merchant_decision, + // payment_confirm_source, + // updated_by, + // surcharge_applicable, + // authorization_count, + // session_expiry, + // request_external_three_ds_authentication, + // frm_metadata, + // customer_details: customer_details.map(Encryption::from), + // billing_address: billing_address.map(Encryption::from), + // merchant_order_reference_id, + // shipping_address: shipping_address.map(Encryption::from), + // is_payment_processor_token_flow, + // } } } @@ -1585,64 +1481,111 @@ impl behaviour::Conversion for PaymentIntent { type NewDstType = DieselPaymentIntentNew; async fn convert(self) -> CustomResult { + let PaymentIntent { + merchant_id, + amount_details, + status, + amount_captured, + customer_id, + description, + return_url, + metadata, + statement_descriptor, + created_at, + modified_at, + last_synced, + setup_future_usage, + client_secret, + active_attempt, + order_details, + allowed_payment_method_types, + connector_metadata, + feature_metadata, + attempt_count, + profile_id, + payment_link_id, + frm_merchant_decision, + updated_by, + request_incremental_authorization, + authorization_count, + session_expiry, + request_external_three_ds_authentication, + frm_metadata, + customer_details, + merchant_reference_id, + billing_address, + shipping_address, + capture_method, + id, + authentication_type, + prerouting_algorithm, + organization_id, + enable_payment_link, + apply_mit_exemption, + customer_present, + routing_algorithm_id, + payment_link_config, + } = self; Ok(DieselPaymentIntent { - merchant_id: self.merchant_id, - status: self.status, - amount: self.amount, - currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - statement_descriptor_name: self.statement_descriptor_name, - created_at: self.created_at, - modified_at: self.modified_at, - last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, - connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - frm_merchant_decision: self.frm_merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, - updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - authorization_count: self.authorization_count, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - frm_metadata: self.frm_metadata, - customer_details: self.customer_details.map(Encryption::from), - billing_address: self.billing_address.map(Encryption::from), - merchant_order_reference_id: self.merchant_order_reference_id, - shipping_address: self.shipping_address.map(Encryption::from), - is_payment_processor_token_flow: self.is_payment_processor_token_flow, - capture_method: self.capture_method, - id: self.id, - authentication_type: self.authentication_type, - amount_to_capture: self.amount_to_capture, - prerouting_algorithm: self.prerouting_algorithm, - merchant_reference_id: self.merchant_reference_id, - surcharge_amount: self.surcharge_amount, - tax_on_surcharge: self.tax_on_surcharge, - organization_id: self.organization_id, - shipping_cost: self.shipping_cost, - tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, + skip_external_tax_calculation: Some(amount_details.get_external_tax_action_as_bool()), + surcharge_applicable: Some(amount_details.get_surcharge_action_as_bool()), + merchant_id, + status, + amount: amount_details.order_amount, + currency: amount_details.currency, + amount_captured, + customer_id, + description, + return_url, + metadata, + statement_descriptor, + created_at, + modified_at, + last_synced, + setup_future_usage, + client_secret, + active_attempt_id: active_attempt.get_id(), + order_details, + allowed_payment_method_types, + connector_metadata, + feature_metadata, + attempt_count, + profile_id, + frm_merchant_decision, + payment_link_id, + updated_by, + + request_incremental_authorization, + authorization_count, + session_expiry, + request_external_three_ds_authentication: Some( + request_external_three_ds_authentication.as_bool(), + ), + frm_metadata, + customer_details: customer_details.map(Encryption::from), + billing_address: billing_address.map(Encryption::from), + shipping_address: shipping_address.map(Encryption::from), + capture_method, + id, + authentication_type, + prerouting_algorithm, + merchant_reference_id, + surcharge_amount: amount_details.surcharge_amount, + tax_on_surcharge: amount_details.tax_on_surcharge, + organization_id, + shipping_cost: amount_details.shipping_cost, + tax_details: amount_details.tax_details, + enable_payment_link: Some(enable_payment_link.as_bool()), + apply_mit_exemption: Some(apply_mit_exemption.as_bool()), + customer_present: Some(customer_present.as_bool()), + payment_link_config, + routing_algorithm_id, }) } async fn convert_back( state: &KeyManagerState, storage_model: Self::DstType, - key: &Secret>, + key: &masking::Secret>, key_manager_identifier: keymanager::Identifier, ) -> CustomResult where @@ -1660,22 +1603,36 @@ impl behaviour::Conversion for PaymentIntent { .await .and_then(|val| val.try_into_optionaloperation()) }; + + let amount_details = super::AmountDetails { + order_amount: storage_model.amount, + currency: storage_model.currency, + surcharge_amount: storage_model.surcharge_amount, + tax_on_surcharge: storage_model.tax_on_surcharge, + shipping_cost: storage_model.shipping_cost, + tax_details: storage_model.tax_details, + skip_external_tax_calculation: super::TaxCalculationOverride::from( + storage_model.skip_external_tax_calculation, + ), + skip_surcharge_calculation: super::SurchargeCalculationOverride::from( + storage_model.surcharge_applicable, + ), + }; + Ok::>(Self { merchant_id: storage_model.merchant_id, status: storage_model.status, - amount: storage_model.amount, - currency: storage_model.currency, + amount_details, amount_captured: storage_model.amount_captured, customer_id: storage_model.customer_id, description: storage_model.description, return_url: storage_model.return_url, metadata: storage_model.metadata, - statement_descriptor_name: storage_model.statement_descriptor_name, + statement_descriptor: storage_model.statement_descriptor, created_at: storage_model.created_at, modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, setup_future_usage: storage_model.setup_future_usage, - off_session: storage_model.off_session, client_secret: storage_model.client_secret, active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), order_details: storage_model.order_details, @@ -1686,15 +1643,14 @@ impl behaviour::Conversion for PaymentIntent { profile_id: storage_model.profile_id, frm_merchant_decision: storage_model.frm_merchant_decision, payment_link_id: storage_model.payment_link_id, - payment_confirm_source: storage_model.payment_confirm_source, updated_by: storage_model.updated_by, - surcharge_applicable: storage_model.surcharge_applicable, request_incremental_authorization: storage_model.request_incremental_authorization, authorization_count: storage_model.authorization_count, session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: storage_model - .request_external_three_ds_authentication, - charges: storage_model.charges, + request_external_three_ds_authentication: + common_enums::External3dsAuthenticationRequest::from( + storage_model.request_external_three_ds_authentication, + ), frm_metadata: storage_model.frm_metadata, customer_details: storage_model .customer_details @@ -1704,24 +1660,27 @@ impl behaviour::Conversion for PaymentIntent { .billing_address .async_lift(inner_decrypt) .await?, - merchant_order_reference_id: storage_model.merchant_order_reference_id, shipping_address: storage_model .shipping_address .async_lift(inner_decrypt) .await?, - is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, capture_method: storage_model.capture_method, id: storage_model.id, merchant_reference_id: storage_model.merchant_reference_id, organization_id: storage_model.organization_id, authentication_type: storage_model.authentication_type, - amount_to_capture: storage_model.amount_to_capture, prerouting_algorithm: storage_model.prerouting_algorithm, - surcharge_amount: storage_model.surcharge_amount, - tax_on_surcharge: storage_model.tax_on_surcharge, - shipping_cost: storage_model.shipping_cost, - tax_details: storage_model.tax_details, - skip_external_tax_calculation: storage_model.skip_external_tax_calculation, + enable_payment_link: common_enums::EnablePaymentLinkRequest::from( + storage_model.enable_payment_link, + ), + apply_mit_exemption: common_enums::MitExemptionRequest::from( + storage_model.apply_mit_exemption, + ), + customer_present: common_enums::PresenceOfCustomerDuringPayment::from( + storage_model.customer_present, + ), + payment_link_config: storage_model.payment_link_config, + routing_algorithm_id: storage_model.routing_algorithm_id, }) } .await @@ -1731,22 +1690,25 @@ impl behaviour::Conversion for PaymentIntent { } async fn construct_new(self) -> CustomResult { + let amount_details = self.amount_details; + Ok(DieselPaymentIntentNew { + surcharge_applicable: Some(amount_details.get_surcharge_action_as_bool()), + skip_external_tax_calculation: Some(amount_details.get_external_tax_action_as_bool()), merchant_id: self.merchant_id, status: self.status, - amount: self.amount, - currency: self.currency, + amount: amount_details.order_amount, + currency: amount_details.currency, amount_captured: self.amount_captured, customer_id: self.customer_id, description: self.description, return_url: self.return_url, metadata: self.metadata, - statement_descriptor_name: self.statement_descriptor_name, + statement_descriptor: self.statement_descriptor, created_at: self.created_at, modified_at: self.modified_at, last_synced: self.last_synced, setup_future_usage: self.setup_future_usage, - off_session: self.off_session, client_secret: self.client_secret, active_attempt_id: self.active_attempt.get_id(), order_details: self.order_details, @@ -1757,32 +1719,30 @@ impl behaviour::Conversion for PaymentIntent { profile_id: self.profile_id, frm_merchant_decision: self.frm_merchant_decision, payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, authorization_count: self.authorization_count, session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, + request_external_three_ds_authentication: Some( + self.request_external_three_ds_authentication.as_bool(), + ), frm_metadata: self.frm_metadata, customer_details: self.customer_details.map(Encryption::from), billing_address: self.billing_address.map(Encryption::from), - merchant_order_reference_id: self.merchant_order_reference_id, shipping_address: self.shipping_address.map(Encryption::from), - is_payment_processor_token_flow: self.is_payment_processor_token_flow, capture_method: self.capture_method, id: self.id, merchant_reference_id: self.merchant_reference_id, authentication_type: self.authentication_type, - amount_to_capture: self.amount_to_capture, prerouting_algorithm: self.prerouting_algorithm, - surcharge_amount: self.surcharge_amount, - tax_on_surcharge: self.tax_on_surcharge, + surcharge_amount: amount_details.surcharge_amount, + tax_on_surcharge: amount_details.tax_on_surcharge, organization_id: self.organization_id, - shipping_cost: self.shipping_cost, - tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, + shipping_cost: amount_details.shipping_cost, + tax_details: amount_details.tax_details, + enable_payment_link: Some(self.enable_payment_link.as_bool()), + apply_mit_exemption: Some(self.apply_mit_exemption.as_bool()), }) } } diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index a325f41404d..fc7d717d5f1 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -656,7 +656,7 @@ impl CustomerDeleteBridge for customers::GlobalId { name: Some(redacted_encrypted_value.clone()), email: Box::new(Some(redacted_encrypted_email)), phone: Box::new(Some(redacted_encrypted_value.clone())), - description: Some(Description::new(REDACTED.to_string())), + description: Some(Description::from_str_unchecked(REDACTED)), phone_country_code: Some(REDACTED.to_string()), metadata: None, connector_customer: None, @@ -898,7 +898,7 @@ impl CustomerDeleteBridge for customers::CustomerId { .switch()?, ), phone: Box::new(Some(redacted_encrypted_value.clone())), - description: Some(Description::new(REDACTED.to_string())), + description: Some(Description::from_str_unchecked(REDACTED)), phone_country_code: Some(REDACTED.to_string()), metadata: None, connector_customer: None, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 7e184b318da..c696c5072bf 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -47,6 +47,32 @@ pub mod flows; pub mod operation; pub mod types; +#[cfg(feature = "v2")] +#[instrument(skip_all)] +pub async fn call_frm_service( + state: &SessionState, + payment_data: &OperationData, + frm_data: &mut FrmData, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + customer: &Option, +) -> RouterResult> +where + F: Send + Clone, + + OperationData: payments::OperationSessionGetters + Send + Sync + Clone, + + // To create connector flow specific interface data + FrmData: ConstructFlowSpecificData, + oss_types::RouterData: FeatureFrm + Send, + + // To construct connector flow specific api + dyn Connector: services::api::ConnectorIntegration, +{ + todo!() +} + +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn call_frm_service( state: &SessionState, @@ -350,6 +376,30 @@ where } } +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments)] +pub async fn make_frm_data_and_fraud_check_operation<'a, F, D>( + _db: &dyn StorageInterface, + state: &SessionState, + merchant_account: &domain::MerchantAccount, + payment_data: D, + frm_routing_algorithm: FrmRoutingAlgorithm, + profile_id: common_utils::id_type::ProfileId, + frm_configs: FrmConfigsObject, + _customer: &Option, +) -> RouterResult> +where + F: Send + Clone, + D: payments::OperationSessionGetters + + payments::OperationSessionSetters + + Send + + Sync + + Clone, +{ + todo!() +} + +#[cfg(feature = "v1")] #[allow(clippy::too_many_arguments)] pub async fn make_frm_data_and_fraud_check_operation<'a, F, D>( _db: &dyn StorageInterface, diff --git a/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs b/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs index f3eab2dd8d1..87ede498ab4 100644 --- a/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs +++ b/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs @@ -16,6 +16,20 @@ use crate::{ utils, SessionState, }; +#[cfg(feature = "v2")] +pub async fn construct_fulfillment_router_data<'a>( + _state: &'a SessionState, + _payment_intent: &'a storage::PaymentIntent, + _payment_attempt: &storage::PaymentAttempt, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _connector: String, + _fulfillment_request: FrmFulfillmentRequest, +) -> RouterResult { + todo!() +} + +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn construct_fulfillment_router_data<'a>( state: &'a SessionState, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index 1a8bc1b5540..65079f633df 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -85,6 +85,17 @@ where #[async_trait] impl GetTracker for FraudCheckPost { + #[cfg(feature = "v2")] + async fn get_trackers<'a>( + &'a self, + state: &'a SessionState, + payment_data: PaymentToFrmData, + frm_connector_details: ConnectorDetailsCore, + ) -> RouterResult> { + todo!() + } + + #[cfg(feature = "v1")] #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs index 666028e5dfd..59a703d2b3c 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs @@ -71,6 +71,18 @@ where #[async_trait] impl GetTracker for FraudCheckPre { + #[cfg(feature = "v2")] + #[instrument(skip_all)] + async fn get_trackers<'a>( + &'a self, + state: &'a SessionState, + payment_data: PaymentToFrmData, + frm_connector_details: ConnectorDetailsCore, + ) -> RouterResult> { + todo!() + } + + #[cfg(feature = "v1")] #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 8e3d2799fd4..e51771a2807 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1317,10 +1317,12 @@ async fn get_pm_list_context( hyperswitch_token_data: is_payment_associated.then_some( storage::PaymentTokenData::permanent_card( Some(pm.get_id().clone()), - pm.locker_id.clone().or(Some(pm.get_id().get_string_repr())), pm.locker_id .clone() - .unwrap_or(pm.get_id().get_string_repr()), + .or(Some(pm.get_id().get_string_repr().to_owned())), + pm.locker_id + .clone() + .unwrap_or(pm.get_id().get_string_repr().to_owned()), ), ), }) @@ -1628,7 +1630,7 @@ async fn generate_saved_pm_response( let pma = api::CustomerPaymentMethod { payment_token: parent_payment_method_token.clone(), - payment_method_id: pm.get_id().get_string_repr(), + payment_method_id: pm.get_id().get_string_repr().to_owned(), customer_id: pm.customer_id.to_owned(), payment_method, payment_method_type: pm.payment_method_type, @@ -1642,7 +1644,7 @@ async fn generate_saved_pm_response( && !(off_session_payment_flag && pm.connector_mandate_details.is_some()), last_used_at: Some(pm.last_used_at), is_default: customer.default_payment_method_id.is_some() - && customer.default_payment_method_id.as_ref() == Some(&pm.get_id().get_string_repr()), + && customer.default_payment_method_id.as_deref() == Some(pm.get_id().get_string_repr()), billing: payment_method_billing, }; @@ -1685,19 +1687,13 @@ impl pm_types::SavedPMLPaymentsInfo { Some(common_enums::FutureUsage::OffSession) ); - let profile_id = payment_intent - .profile_id - .as_ref() - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("profile_id is not set in payment_intent")? - .clone(); + let profile_id = &payment_intent.profile_id; let business_profile = core_utils::validate_and_get_business_profile( db, key_manager_state, key_store, - Some(profile_id).as_ref(), + Some(profile_id), merchant_account.get_id(), ) .await?; diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index efc9621d26d..6e3d89195d2 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -3341,6 +3341,7 @@ pub async fn list_payment_methods( billing_address_for_calculating_required_fields, customer.as_ref(), ))?; + let req_val = serde_json::to_value(req).ok(); logger::debug!(filtered_payment_methods=?response); diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index d0f63a2dc04..6583d109d57 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -546,7 +546,7 @@ pub fn generate_payment_method_response( let resp = api::PaymentMethodResponse { merchant_id: pm.merchant_id.to_owned(), customer_id: pm.customer_id.to_owned(), - payment_method_id: pm.id.get_string_repr(), + payment_method_id: pm.id.get_string_repr().to_owned(), payment_method: pm.payment_method, payment_method_type: pm.payment_method_type, metadata: pm.metadata.clone(), diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 3a55bf77771..744b93032bf 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -608,6 +608,7 @@ where let cloned_payment_data = payment_data.clone(); let cloned_customer = customer.clone(); + #[cfg(feature = "v1")] operation .to_domain()? .store_extended_card_info_temporarily( @@ -704,6 +705,19 @@ where todo!() } +#[cfg(feature = "v2")] +#[instrument(skip_all)] +async fn populate_surcharge_details( + state: &SessionState, + payment_data: &mut PaymentData, +) -> RouterResult<()> +where + F: Send + Clone, +{ + todo!() +} + +#[cfg(feature = "v1")] #[instrument(skip_all)] async fn populate_surcharge_details( state: &SessionState, @@ -1597,6 +1611,7 @@ where ) .await?; + #[cfg(feature = "v1")] let merchant_recipient_data = if let Some(true) = payment_data .get_payment_intent() .is_payment_processor_token_flow @@ -1614,6 +1629,10 @@ where .await? }; + // TODO: handle how we read `is_processor_token_flow` in v2 and then call `get_merchant_recipient_data` + #[cfg(feature = "v2")] + let merchant_recipient_data = None; + let mut router_data = payment_data .construct_router_data( state, @@ -2357,6 +2376,7 @@ where F: Clone, D: OperationSessionGetters + Send + Sync + Clone, { + #[cfg(feature = "v1")] let profile_id = payment_data .get_payment_intent() .profile_id @@ -2366,6 +2386,9 @@ where .attach_printable("profile_id is not set in payment_intent")? .clone(); + #[cfg(feature = "v2")] + let profile_id = payment_data.get_payment_intent().profile_id.clone(); + let merchant_connector_account = helpers::get_merchant_connector_account( state, merchant_account.get_id(), @@ -2735,6 +2758,24 @@ where Ok(payment_data_and_tokenization_action) } +#[cfg(feature = "v2")] +pub async fn tokenize_in_router_when_confirm_false_or_external_authentication( + state: &SessionState, + operation: &BoxedOperation<'_, F, Req, D>, + payment_data: &mut D, + validate_result: &operations::ValidateResult, + merchant_key_store: &domain::MerchantKeyStore, + customer: &Option, + business_profile: &domain::Profile, +) -> RouterResult +where + F: Send + Clone, + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, +{ + todo!() +} + +#[cfg(feature = "v1")] pub async fn tokenize_in_router_when_confirm_false_or_external_authentication( state: &SessionState, operation: &BoxedOperation<'_, F, Req, D>, @@ -3806,7 +3847,7 @@ where .await } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[cfg(feature = "v2")] pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( state: &SessionState, payment_data: &mut D, @@ -3821,10 +3862,7 @@ where todo!() } -#[cfg(all( - any(feature = "v2", feature = "v1"), - not(feature = "payment_methods_v2") -))] +#[cfg(feature = "v1")] #[allow(clippy::too_many_arguments)] pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( state: &SessionState, @@ -4956,6 +4994,7 @@ pub trait OperationSessionGetters { fn get_authorizations(&self) -> Vec; fn get_attempts(&self) -> Option>; fn get_recurring_details(&self) -> Option<&RecurringDetails>; + // TODO: this should be a mandatory field, should we throw an error instead of returning an Option? fn get_payment_intent_profile_id(&self) -> Option<&id_type::ProfileId>; fn get_currency(&self) -> storage_enums::Currency; fn get_amount(&self) -> api::Amount; @@ -5085,10 +5124,16 @@ impl OperationSessionGetters for PaymentData { self.recurring_details.as_ref() } + #[cfg(feature = "v1")] fn get_payment_intent_profile_id(&self) -> Option<&id_type::ProfileId> { self.payment_intent.profile_id.as_ref() } + #[cfg(feature = "v2")] + fn get_payment_intent_profile_id(&self) -> Option<&id_type::ProfileId> { + Some(&self.payment_intent.profile_id) + } + fn get_currency(&self) -> storage_enums::Currency { self.currency } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index ac6c236f536..eb512399233 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1142,6 +1142,7 @@ pub fn validate_customer_id_mandatory_cases( } } +#[cfg(feature = "v1")] pub fn create_startpay_url( base_url: &str, payment_attempt: &PaymentAttempt, @@ -2287,6 +2288,20 @@ pub async fn store_in_vault_and_generate_ppmt( Ok(parent_payment_method_token) } +#[cfg(feature = "v2")] +pub async fn store_payment_method_data_in_vault( + state: &SessionState, + payment_attempt: &PaymentAttempt, + payment_intent: &PaymentIntent, + payment_method: enums::PaymentMethod, + payment_method_data: &domain::PaymentMethodData, + merchant_key_store: &domain::MerchantKeyStore, + business_profile: Option<&domain::Profile>, +) -> RouterResult> { + todo!() +} + +#[cfg(feature = "v1")] pub async fn store_payment_method_data_in_vault( state: &SessionState, payment_attempt: &PaymentAttempt, @@ -2997,6 +3012,7 @@ pub fn generate_mandate( } } +#[cfg(feature = "v1")] // A function to manually authenticate the client secret with intent fulfillment time pub fn authenticate_client_secret( request_client_secret: Option<&String>, @@ -3026,6 +3042,34 @@ pub fn authenticate_client_secret( } } +#[cfg(feature = "v2")] +// A function to manually authenticate the client secret with intent fulfillment time +pub fn authenticate_client_secret( + request_client_secret: Option<&common_utils::types::ClientSecret>, + payment_intent: &PaymentIntent, +) -> Result<(), errors::ApiErrorResponse> { + match (request_client_secret, &payment_intent.client_secret) { + (Some(req_cs), pi_cs) => { + if req_cs != pi_cs { + Err(errors::ApiErrorResponse::ClientSecretInvalid) + } else { + let current_timestamp = common_utils::date_time::now(); + + let session_expiry = payment_intent.session_expiry.unwrap_or( + payment_intent + .created_at + .saturating_add(time::Duration::seconds(consts::DEFAULT_SESSION_EXPIRY)), + ); + + fp_utils::when(current_timestamp > session_expiry, || { + Err(errors::ApiErrorResponse::ClientSecretExpired) + }) + } + } + _ => Ok(()), + } +} + pub(crate) fn validate_payment_status_against_allowed_statuses( intent_status: &storage_enums::IntentStatus, allowed_statuses: &[storage_enums::IntentStatus], @@ -3079,6 +3123,18 @@ pub(crate) fn validate_pm_or_token_given( ) } +#[cfg(feature = "v2")] +// A function to perform database lookup and then verify the client secret +pub async fn verify_payment_intent_time_and_client_secret( + state: &SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + client_secret: Option, +) -> error_stack::Result, errors::ApiErrorResponse> { + todo!() +} + +#[cfg(feature = "v1")] // A function to perform database lookup and then verify the client secret pub async fn verify_payment_intent_time_and_client_secret( state: &SessionState, @@ -3965,9 +4021,8 @@ impl AttemptType { storage_scheme, ) .await - .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { - payment_id: fetched_payment_intent.get_id().to_owned(), - })?; + .to_duplicate_response(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to insert payment attempt")?; let updated_payment_intent = db .update_payment_intent( @@ -5341,6 +5396,7 @@ pub enum PaymentExternalAuthenticationFlow { }, } +#[cfg(feature = "v1")] pub async fn get_payment_external_authentication_flow_during_confirm( state: &SessionState, key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 09292224cae..e0324438b48 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -42,6 +42,7 @@ use crate::{ utils, }; +#[cfg(feature = "v1")] #[derive(Debug, Clone, Copy, router_derive::PaymentOperation)] #[operation( operations = "post_update_tracker", @@ -49,6 +50,11 @@ use crate::{ )] pub struct PaymentResponse; +#[cfg(feature = "v2")] +#[derive(Debug, Clone, Copy)] +pub struct PaymentResponse; + +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsAuthorizeData> for PaymentResponse @@ -304,6 +310,7 @@ impl PostUpdateTracker, types::PaymentsAuthor } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsIncrementalAuthorizationData> for PaymentResponse @@ -460,6 +467,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsSyncData> for PaymentResponse { async fn update_tracker<'b>( @@ -513,6 +521,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsSessionData> for PaymentResponse @@ -545,6 +554,7 @@ impl PostUpdateTracker, types::PaymentsSessionData> } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::SdkPaymentsSessionUpdateData> for PaymentResponse @@ -618,6 +628,7 @@ impl PostUpdateTracker, types::SdkPaymentsSessionUpd } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsCaptureData> for PaymentResponse @@ -650,6 +661,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsCancelData> for PaymentResponse { async fn update_tracker<'b>( @@ -680,6 +692,7 @@ impl PostUpdateTracker, types::PaymentsCancelData> f } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsApproveData> for PaymentResponse @@ -712,6 +725,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::PaymentsRejectData> for PaymentResponse { async fn update_tracker<'b>( @@ -742,6 +756,7 @@ impl PostUpdateTracker, types::PaymentsRejectData> f } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::SetupMandateRequestData> for PaymentResponse @@ -847,6 +862,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa } } +#[cfg(feature = "v1")] #[async_trait] impl PostUpdateTracker, types::CompleteAuthorizeData> for PaymentResponse diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 64a85e3358d..ad643164f11 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -501,9 +501,8 @@ where let payment_attempt = db .insert_payment_attempt(new_payment_attempt, storage_scheme) .await - .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { - payment_id: payment_data.get_payment_intent().get_id().to_owned(), - })?; + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error inserting payment attempt")?; #[cfg(all(feature = "v2", feature = "payment_v2"))] let payment_attempt = db @@ -514,9 +513,8 @@ where storage_scheme, ) .await - .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { - payment_id: payment_data.get_payment_intent().get_id().to_owned(), - })?; + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error inserting payment attempt")?; // update payment_attempt, connector_response and payment_intent in payment_data payment_data.set_payment_attempt(payment_attempt); diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index a3123e91a14..1909fa25fb5 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -819,6 +819,8 @@ pub async fn perform_session_flow_routing( { let mut pm_type_map: FxHashMap> = FxHashMap::default(); + + #[cfg(feature = "v1")] let profile_id = session_input .payment_intent .profile_id @@ -826,6 +828,9 @@ pub async fn perform_session_flow_routing( .get_required_value("profile_id") .change_context(errors::RoutingError::ProfileIdMissing)?; + #[cfg(feature = "v2")] + let profile_id = session_input.payment_intent.profile_id.clone(); + let business_profile = session_input .state .store @@ -885,28 +890,7 @@ pub async fn perform_session_flow_routing( }; #[cfg(feature = "v2")] - let payment_input = dsl_inputs::PaymentInput { - amount: session_input.payment_intent.amount, - currency: session_input - .payment_intent - .currency - .get_required_value("Currency") - .change_context(errors::RoutingError::DslMissingRequiredField { - field_name: "currency".to_string(), - })?, - authentication_type: session_input.payment_attempt.authentication_type, - card_bin: None, - capture_method: session_input - .payment_attempt - .capture_method - .and_then(|cm| cm.foreign_into()), - business_country: None, - business_label: None, - billing_country: session_input - .country - .map(storage_enums::Country::from_alpha2), - setup_future_usage: session_input.payment_intent.setup_future_usage, - }; + let payment_input = todo!(); let metadata = session_input .payment_intent @@ -1155,9 +1139,9 @@ async fn perform_session_routing_for_pm_type( #[cfg(feature = "v2")] pub fn make_dsl_input_for_surcharge( - payment_attempt: &oss_storage::PaymentAttempt, - payment_intent: &oss_storage::PaymentIntent, - billing_address: Option
, + _payment_attempt: &oss_storage::PaymentAttempt, + _payment_intent: &oss_storage::PaymentIntent, + _billing_address: Option
, ) -> RoutingResult { todo!() } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 93fa3ed44b8..22c047edfcb 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -438,6 +438,7 @@ where } } +#[cfg(feature = "v1")] impl ToResponse for api::PaymentsSessionResponse where F: Clone, @@ -472,6 +473,7 @@ where } } +#[cfg(feature = "v1")] impl ToResponse for api::PaymentsDynamicTaxCalculationResponse where F: Clone, @@ -1899,6 +1901,16 @@ impl ConnectorTransactionId for Nexinets { } } +#[cfg(feature = "v2")] +impl TryFrom> for types::PaymentsCaptureData { + type Error = error_stack::Report; + + fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result { + todo!() + } +} + +#[cfg(feature = "v1")] impl TryFrom> for types::PaymentsCaptureData { type Error = error_stack::Report; @@ -1952,6 +1964,16 @@ impl TryFrom> for types::PaymentsCaptureD } } +#[cfg(feature = "v2")] +impl TryFrom> for types::PaymentsCancelData { + type Error = error_stack::Report; + + fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result { + todo!() + } +} + +#[cfg(feature = "v1")] impl TryFrom> for types::PaymentsCancelData { type Error = error_stack::Report; @@ -2002,6 +2024,16 @@ impl TryFrom> for types::PaymentsApproveD } } +#[cfg(feature = "v2")] +impl TryFrom> for types::SdkPaymentsSessionUpdateData { + type Error = error_stack::Report; + + fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result { + todo!() + } +} + +#[cfg(feature = "v1")] impl TryFrom> for types::SdkPaymentsSessionUpdateData { type Error = error_stack::Report; diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 2a93f8c215d..d6d4df67000 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -796,6 +796,18 @@ async fn get_selected_config_from_redis( Ok(selected_config) } +#[cfg(feature = "v2")] +pub async fn retrieve_payment_method_from_auth_service( + state: &SessionState, + key_store: &domain::MerchantKeyStore, + auth_token: &payment_methods::BankAccountTokenData, + payment_intent: &PaymentIntent, + _customer: &Option, +) -> RouterResult> { + todo!() +} + +#[cfg(feature = "v1")] pub async fn retrieve_payment_method_from_auth_service( state: &SessionState, key_store: &domain::MerchantKeyStore, @@ -839,13 +851,6 @@ pub async fn retrieve_payment_method_from_auth_service( "error while fetching merchant_connector_account from merchant_id and connector name", )?; - #[cfg(feature = "v2")] - let mca = { - let _ = merchant_account; - let _ = connector; - todo!() - }; - let auth_type = pm_auth_helpers::get_connector_auth_type(mca)?; let BankAccountAccessCreds::AccessToken(access_token) = diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 75630da14ce..53d97b43d9c 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -220,6 +220,25 @@ pub async fn construct_payout_router_data<'a, F>( Ok(router_data) } +#[cfg(feature = "v2")] +#[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] +pub async fn construct_refund_router_data<'a, F>( + _state: &'a SessionState, + _connector_id: &str, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _money: (MinorUnit, enums::Currency), + _payment_intent: &'a storage::PaymentIntent, + _payment_attempt: &storage::PaymentAttempt, + _refund: &'a storage::Refund, + _creds_identifier: Option, + _charges: Option, +) -> RouterResult> { + todo!() +} + +#[cfg(feature = "v1")] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn construct_refund_router_data<'a, F>( @@ -588,6 +607,7 @@ pub fn validate_dispute_stage_and_dispute_status( ) } +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn construct_accept_dispute_router_data<'a>( state: &'a SessionState, @@ -681,6 +701,7 @@ pub async fn construct_accept_dispute_router_data<'a>( Ok(router_data) } +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn construct_submit_evidence_router_data<'a>( state: &'a SessionState, @@ -773,6 +794,7 @@ pub async fn construct_submit_evidence_router_data<'a>( Ok(router_data) } +#[cfg(feature = "v1")] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn construct_upload_file_router_data<'a>( @@ -871,6 +893,18 @@ pub async fn construct_upload_file_router_data<'a>( Ok(router_data) } +#[cfg(feature = "v2")] +pub async fn construct_payments_dynamic_tax_calculation_router_data<'a, F: Clone>( + state: &'a SessionState, + merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + payment_data: &mut PaymentData, + merchant_connector_account: &MerchantConnectorAccount, +) -> RouterResult { + todo!() +} + +#[cfg(feature = "v1")] pub async fn construct_payments_dynamic_tax_calculation_router_data<'a, F: Clone>( state: &'a SessionState, merchant_account: &domain::MerchantAccount, @@ -978,6 +1012,7 @@ pub async fn construct_payments_dynamic_tax_calculation_router_data<'a, F: Clone Ok(router_data) } +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn construct_defend_dispute_router_data<'a>( state: &'a SessionState, @@ -1459,9 +1494,16 @@ impl GetProfileId for MerchantConnectorAccount { } impl GetProfileId for storage::PaymentIntent { + #[cfg(feature = "v1")] fn get_profile_id(&self) -> Option<&common_utils::id_type::ProfileId> { self.profile_id.as_ref() } + + // TODO: handle this in a better way for v2 + #[cfg(feature = "v2")] + fn get_profile_id(&self) -> Option<&common_utils::id_type::ProfileId> { + Some(&self.profile_id) + } } impl GetProfileId for (storage::PaymentIntent, A) { diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index 787220651f1..8193109c250 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -130,13 +130,23 @@ pub fn log_applepay_verification_response_if_error( .map_err(|error| logger::error!(applepay_domain_verification_error= ?error)) }); } + +#[cfg(feature = "v2")] +pub async fn check_if_profile_id_is_present_in_payment_intent( + payment_id: PaymentId, + state: &SessionState, + auth_data: &AuthenticationData, +) -> CustomResult<(), errors::ApiErrorResponse> { + todo!() +} + +#[cfg(feature = "v1")] pub async fn check_if_profile_id_is_present_in_payment_intent( payment_id: PaymentId, state: &SessionState, auth_data: &AuthenticationData, ) -> CustomResult<(), errors::ApiErrorResponse> { let db = &*state.store; - #[cfg(feature = "v1")] let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( &state.into(), @@ -148,15 +158,5 @@ pub async fn check_if_profile_id_is_present_in_payment_intent( .await .change_context(errors::ApiErrorResponse::Unauthorized)?; - #[cfg(feature = "v2")] - let payment_intent = db - .find_payment_intent_by_id( - &state.into(), - &payment_id, - &auth_data.key_store, - auth_data.merchant_account.storage_scheme, - ) - .await - .change_context(errors::ApiErrorResponse::Unauthorized)?; utils::validate_profile_id_from_auth_layer(auth_data.profile_id.clone(), &payment_intent) } diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index ff7e8145c33..21e00a7f549 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1718,7 +1718,7 @@ impl PaymentIntentInterface for KafkaStore { async fn find_payment_intent_by_id( &self, state: &KeyManagerState, - payment_id: &id_type::PaymentId, + payment_id: &id_type::GlobalPaymentId, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { diff --git a/crates/router/src/services/kafka/payment_intent.rs b/crates/router/src/services/kafka/payment_intent.rs index cb5ec9cf2e3..82e33de8c60 100644 --- a/crates/router/src/services/kafka/payment_intent.rs +++ b/crates/router/src/services/kafka/payment_intent.rs @@ -51,13 +51,13 @@ pub struct KafkaPaymentIntent<'a> { pub merchant_id: &'a id_type::MerchantId, pub status: storage_enums::IntentStatus, pub amount: MinorUnit, - pub currency: Option, + pub currency: storage_enums::Currency, pub amount_captured: Option, pub customer_id: Option<&'a id_type::CustomerId>, pub description: Option<&'a String>, pub return_url: Option<&'a String>, pub metadata: Option, - pub statement_descriptor_name: Option<&'a String>, + pub statement_descriptor: Option<&'a String>, #[serde(with = "time::serde::timestamp")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::timestamp")] @@ -69,7 +69,7 @@ pub struct KafkaPaymentIntent<'a> { pub client_secret: Option<&'a String>, pub active_attempt_id: String, pub attempt_count: i16, - pub profile_id: Option<&'a id_type::ProfileId>, + pub profile_id: &'a id_type::ProfileId, pub payment_confirm_source: Option, pub billing_details: Option>>, pub shipping_details: Option>>, @@ -128,42 +128,43 @@ impl<'a> KafkaPaymentIntent<'a> { #[cfg(feature = "v2")] impl<'a> KafkaPaymentIntent<'a> { pub fn from_storage(intent: &'a PaymentIntent) -> Self { - Self { - id: &intent.id, - merchant_id: &intent.merchant_id, - status: intent.status, - amount: intent.amount, - currency: intent.currency, - amount_captured: intent.amount_captured, - customer_id: intent.customer_id.as_ref(), - description: intent.description.as_ref(), - return_url: intent.return_url.as_ref(), - metadata: intent.metadata.as_ref().map(|x| x.to_string()), - statement_descriptor_name: intent.statement_descriptor_name.as_ref(), - created_at: intent.created_at.assume_utc(), - modified_at: intent.modified_at.assume_utc(), - last_synced: intent.last_synced.map(|i| i.assume_utc()), - setup_future_usage: intent.setup_future_usage, - off_session: intent.off_session, - client_secret: intent.client_secret.as_ref(), - active_attempt_id: intent.active_attempt.get_id(), - attempt_count: intent.attempt_count, - profile_id: intent.profile_id.as_ref(), - payment_confirm_source: intent.payment_confirm_source, - // TODO: use typed information here to avoid PII logging - billing_details: None, - shipping_details: None, - customer_email: intent - .customer_details - .as_ref() - .and_then(|value| value.get_inner().peek().as_object()) - .and_then(|obj| obj.get("email")) - .and_then(|email| email.as_str()) - .map(|email| HashedString::from(Secret::new(email.to_string()))), - feature_metadata: intent.feature_metadata.as_ref(), - merchant_order_reference_id: intent.merchant_order_reference_id.as_ref(), - organization_id: &intent.organization_id, - } + // Self { + // id: &intent.id, + // merchant_id: &intent.merchant_id, + // status: intent.status, + // amount: intent.amount, + // currency: intent.currency, + // amount_captured: intent.amount_captured, + // customer_id: intent.customer_id.as_ref(), + // description: intent.description.as_ref(), + // return_url: intent.return_url.as_ref(), + // metadata: intent.metadata.as_ref().map(|x| x.to_string()), + // statement_descriptor: intent.statement_descriptor.as_ref(), + // created_at: intent.created_at.assume_utc(), + // modified_at: intent.modified_at.assume_utc(), + // last_synced: intent.last_synced.map(|i| i.assume_utc()), + // setup_future_usage: intent.setup_future_usage, + // off_session: intent.off_session, + // client_secret: intent.client_secret.as_ref(), + // active_attempt_id: intent.active_attempt.get_id(), + // attempt_count: intent.attempt_count, + // profile_id: &intent.profile_id, + // payment_confirm_source: intent.payment_confirm_source, + // // TODO: use typed information here to avoid PII logging + // billing_details: None, + // shipping_details: None, + // customer_email: intent + // .customer_details + // .as_ref() + // .and_then(|value| value.get_inner().peek().as_object()) + // .and_then(|obj| obj.get("email")) + // .and_then(|email| email.as_str()) + // .map(|email| HashedString::from(Secret::new(email.to_string()))), + // feature_metadata: intent.feature_metadata.as_ref(), + // merchant_order_reference_id: intent.merchant_order_reference_id.as_ref(), + // organization_id: &intent.organization_id, + // } + todo!() } } diff --git a/crates/router/src/services/kafka/payment_intent_event.rs b/crates/router/src/services/kafka/payment_intent_event.rs index 321ce558103..5657846ca4c 100644 --- a/crates/router/src/services/kafka/payment_intent_event.rs +++ b/crates/router/src/services/kafka/payment_intent_event.rs @@ -53,13 +53,13 @@ pub struct KafkaPaymentIntentEvent<'a> { pub merchant_id: &'a id_type::MerchantId, pub status: storage_enums::IntentStatus, pub amount: MinorUnit, - pub currency: Option, + pub currency: storage_enums::Currency, pub amount_captured: Option, pub customer_id: Option<&'a id_type::CustomerId>, pub description: Option<&'a String>, pub return_url: Option<&'a String>, pub metadata: Option, - pub statement_descriptor_name: Option<&'a String>, + pub statement_descriptor: Option<&'a String>, #[serde(with = "time::serde::timestamp::milliseconds")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::timestamp::milliseconds")] @@ -71,7 +71,7 @@ pub struct KafkaPaymentIntentEvent<'a> { pub client_secret: Option<&'a String>, pub active_attempt_id: String, pub attempt_count: i16, - pub profile_id: Option<&'a id_type::ProfileId>, + pub profile_id: &'a id_type::ProfileId, pub payment_confirm_source: Option, pub billing_details: Option>>, pub shipping_details: Option>>, @@ -142,42 +142,43 @@ impl<'a> KafkaPaymentIntentEvent<'a> { #[cfg(feature = "v2")] impl<'a> KafkaPaymentIntentEvent<'a> { pub fn from_storage(intent: &'a PaymentIntent) -> Self { - Self { - id: &intent.id, - merchant_id: &intent.merchant_id, - status: intent.status, - amount: intent.amount, - currency: intent.currency, - amount_captured: intent.amount_captured, - customer_id: intent.customer_id.as_ref(), - description: intent.description.as_ref(), - return_url: intent.return_url.as_ref(), - metadata: intent.metadata.as_ref().map(|x| x.to_string()), - statement_descriptor_name: intent.statement_descriptor_name.as_ref(), - created_at: intent.created_at.assume_utc(), - modified_at: intent.modified_at.assume_utc(), - last_synced: intent.last_synced.map(|i| i.assume_utc()), - setup_future_usage: intent.setup_future_usage, - off_session: intent.off_session, - client_secret: intent.client_secret.as_ref(), - active_attempt_id: intent.active_attempt.get_id(), - attempt_count: intent.attempt_count, - profile_id: intent.profile_id.as_ref(), - payment_confirm_source: intent.payment_confirm_source, - // TODO: use typed information here to avoid PII logging - billing_details: None, - shipping_details: None, - customer_email: intent - .customer_details - .as_ref() - .and_then(|value| value.get_inner().peek().as_object()) - .and_then(|obj| obj.get("email")) - .and_then(|email| email.as_str()) - .map(|email| HashedString::from(Secret::new(email.to_string()))), - feature_metadata: intent.feature_metadata.as_ref(), - merchant_order_reference_id: intent.merchant_order_reference_id.as_ref(), - organization_id: &intent.organization_id, - } + // Self { + // id: &intent.id, + // merchant_id: &intent.merchant_id, + // status: intent.status, + // amount: intent.amount, + // currency: intent.currency, + // amount_captured: intent.amount_captured, + // customer_id: intent.customer_id.as_ref(), + // description: intent.description.as_ref(), + // return_url: intent.return_url.as_ref(), + // metadata: intent.metadata.as_ref().map(|x| x.to_string()), + // statement_descriptor: intent.statement_descriptor.as_ref(), + // created_at: intent.created_at.assume_utc(), + // modified_at: intent.modified_at.assume_utc(), + // last_synced: intent.last_synced.map(|i| i.assume_utc()), + // setup_future_usage: intent.setup_future_usage, + // off_session: intent.off_session, + // client_secret: intent.client_secret.as_ref(), + // active_attempt_id: intent.active_attempt.get_id(), + // attempt_count: intent.attempt_count, + // profile_id: &intent.profile_id, + // payment_confirm_source: intent.payment_confirm_source, + // // TODO: use typed information here to avoid PII logging + // billing_details: None, + // shipping_details: None, + // customer_email: intent + // .customer_details + // .as_ref() + // .and_then(|value| value.get_inner().peek().as_object()) + // .and_then(|obj| obj.get("email")) + // .and_then(|email| email.as_str()) + // .map(|email| HashedString::from(Secret::new(email.to_string()))), + // feature_metadata: intent.feature_metadata.as_ref(), + // merchant_order_reference_id: intent.merchant_order_reference_id.as_ref(), + // organization_id: &intent.organization_id, + // } + todo!() } } diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 96656423897..13a5291062c 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -48,7 +48,7 @@ pub use diesel_models::{ }; pub use hyperswitch_domain_models::payments::{ payment_attempt::{PaymentAttempt, PaymentAttemptNew, PaymentAttemptUpdate}, - payment_intent::{PaymentIntentNew, PaymentIntentUpdate, PaymentIntentUpdateFields}, + payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields}, PaymentIntent, }; #[cfg(feature = "payouts")] diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index edeb3f12fa7..e6f549f4fc4 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -370,6 +370,7 @@ pub async fn find_mca_from_authentication_id_type( } } +#[cfg(feature = "v1")] pub async fn get_mca_from_payment_intent( state: &SessionState, merchant_account: &domain::MerchantAccount, diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index cadb78944ee..974de5be802 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -332,12 +332,9 @@ impl UniqueConstraints for diesel_models::Address { #[cfg(all(feature = "v2", feature = "payment_v2"))] impl UniqueConstraints for diesel_models::PaymentIntent { fn unique_constraints(&self) -> Vec { - vec![format!( - "pi_{}_{}", - self.merchant_id.get_string_repr(), - self.merchant_reference_id - )] + vec![self.id.get_string_repr().to_owned()] } + fn table_name(&self) -> &str { "PaymentIntent" } @@ -345,6 +342,7 @@ impl UniqueConstraints for diesel_models::PaymentIntent { #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] impl UniqueConstraints for diesel_models::PaymentIntent { + #[cfg(feature = "v1")] fn unique_constraints(&self) -> Vec { vec![format!( "pi_{}_{}", @@ -352,6 +350,12 @@ impl UniqueConstraints for diesel_models::PaymentIntent { self.payment_id.get_string_repr() )] } + + #[cfg(feature = "v2")] + fn unique_constraints(&self) -> Vec { + vec![format!("pi_{}", self.id.get_string_repr())] + } + fn table_name(&self) -> &str { "PaymentIntent" } @@ -437,7 +441,7 @@ impl UniqueConstraints for diesel_models::PaymentMethod { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl UniqueConstraints for diesel_models::PaymentMethod { fn unique_constraints(&self) -> Vec { - vec![self.id.get_string_repr()] + vec![self.id.get_string_repr().to_owned()] } fn table_name(&self) -> &str { "PaymentMethod" diff --git a/crates/storage_impl/src/mock_db/payment_intent.rs b/crates/storage_impl/src/mock_db/payment_intent.rs index e0328871e33..0f1161f644b 100644 --- a/crates/storage_impl/src/mock_db/payment_intent.rs +++ b/crates/storage_impl/src/mock_db/payment_intent.rs @@ -167,7 +167,7 @@ impl PaymentIntentInterface for MockDb { async fn find_payment_intent_by_id( &self, _state: &KeyManagerState, - id: &common_utils::id_type::PaymentId, + id: &common_utils::id_type::GlobalPaymentId, _merchant_key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result { diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 97fa5873cae..971c5edfcdb 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -65,6 +65,7 @@ use crate::{ #[async_trait::async_trait] impl PaymentIntentInterface for KVRouterStore { + #[cfg(feature = "v1")] async fn insert_payment_intent( &self, state: &KeyManagerState, @@ -142,6 +143,33 @@ impl PaymentIntentInterface for KVRouterStore { } } + #[cfg(feature = "v2")] + async fn insert_payment_intent( + &self, + state: &KeyManagerState, + payment_intent: PaymentIntent, + merchant_key_store: &MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> error_stack::Result { + match storage_scheme { + MerchantStorageScheme::PostgresOnly => { + self.router_store + .insert_payment_intent( + state, + payment_intent, + merchant_key_store, + storage_scheme, + ) + .await + } + + MerchantStorageScheme::RedisKv => { + todo!("Implement payment intent insert for kv") + } + } + } + + #[cfg(feature = "v1")] #[instrument(skip_all)] async fn update_payment_intent( &self, @@ -229,6 +257,34 @@ impl PaymentIntentInterface for KVRouterStore { } } + #[cfg(feature = "v2")] + #[instrument(skip_all)] + async fn update_payment_intent( + &self, + state: &KeyManagerState, + this: PaymentIntent, + payment_intent_update: PaymentIntentUpdate, + merchant_key_store: &MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> error_stack::Result { + match storage_scheme { + MerchantStorageScheme::PostgresOnly => { + self.router_store + .update_payment_intent( + state, + this, + payment_intent_update, + merchant_key_store, + storage_scheme, + ) + .await + } + MerchantStorageScheme::RedisKv => { + todo!() + } + } + } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] #[instrument(skip_all)] async fn find_payment_intent_by_payment_id_merchant_id( @@ -294,11 +350,14 @@ impl PaymentIntentInterface for KVRouterStore { async fn find_payment_intent_by_id( &self, state: &KeyManagerState, - id: &common_utils::id_type::PaymentId, + id: &common_utils::id_type::GlobalPaymentId, merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { - let conn = pg_connection_read(self).await?; + let conn: bb8::PooledConnection< + '_, + async_bb8_diesel::ConnectionManager, + > = pg_connection_read(self).await?; let diesel_payment_intent = DieselPaymentIntent::find_by_global_id(&conn, id) .await .map_err(|er| { @@ -555,7 +614,7 @@ impl PaymentIntentInterface for crate::RouterStore { async fn find_payment_intent_by_id( &self, state: &KeyManagerState, - id: &common_utils::id_type::PaymentId, + id: &common_utils::id_type::GlobalPaymentId, merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { 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 427dbcb4d5f..de4886b866f 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 @@ -24,4 +24,10 @@ ALTER TABLE payment_intent DROP COLUMN merchant_reference_id, DROP COLUMN prerouting_algorithm, DROP COLUMN surcharge_amount, DROP COLUMN tax_on_surcharge, - DROP COLUMN frm_merchant_decision; + DROP COLUMN frm_merchant_decision, + DROP COLUMN statement_descriptor, + DROP COLUMN enable_payment_link, + DROP COLUMN apply_mit_exemption, + DROP COLUMN customer_present, + DROP COLUMN routing_algorithm_id, + DROP COLUMN payment_link_config; 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 719a8afa30e..b89985ae815 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 @@ -5,6 +5,7 @@ ADD COLUMN IF NOT EXISTS merchant_reference_id VARCHAR(64), ADD COLUMN IF NOT EXISTS default_billing_address BYTEA DEFAULT NULL, ADD COLUMN IF NOT EXISTS default_shipping_address BYTEA DEFAULT NULL, ADD COLUMN IF NOT EXISTS status "DeleteStatus" NOT NULL DEFAULT 'active'; + CREATE TYPE "OrderFulfillmentTimeOrigin" AS ENUM ('create', 'confirm'); ALTER TABLE business_profile @@ -16,14 +17,19 @@ ADD COLUMN routing_algorithm_id VARCHAR(64) DEFAULT NULL, ADD COLUMN default_fallback_routing JSONB DEFAULT NULL; ALTER TABLE payment_intent -ADD COLUMN merchant_reference_id VARCHAR(64) NOT NULL, +ADD COLUMN merchant_reference_id VARCHAR(64), ADD COLUMN billing_address BYTEA DEFAULT NULL, ADD COLUMN shipping_address BYTEA DEFAULT NULL, ADD COLUMN capture_method "CaptureMethod", ADD COLUMN authentication_type "AuthenticationType", ADD COLUMN amount_to_capture bigint, - ADD COLUMN prerouting_algorithm JSONB, -- straight_through_algorithm from payment_attempt + ADD COLUMN prerouting_algorithm JSONB, ADD COLUMN surcharge_amount bigint, - ADD COLUMN tax_on_surcharge bigint, -- tax_amount from payment_attempt - ADD COLUMN frm_merchant_decision VARCHAR(64); - + ADD COLUMN tax_on_surcharge bigint, + ADD COLUMN frm_merchant_decision VARCHAR(64), + ADD COLUMN statement_descriptor VARCHAR(255), + ADD COLUMN enable_payment_link BOOLEAN, + ADD COLUMN apply_mit_exemption BOOLEAN, + ADD COLUMN customer_present BOOLEAN, + ADD COLUMN routing_algorithm_id VARCHAR(64), + ADD COLUMN payment_link_config JSONB; diff --git a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql index 6acb08597f5..50f0e89da8c 100644 --- a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql +++ b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql @@ -1,4 +1,5 @@ -- Backfill for organization table +------------------------ Organization ----------------------- UPDATE ORGANIZATION SET org_id = id WHERE org_id IS NULL; @@ -16,6 +17,7 @@ SET org_name = organization_name WHERE org_name IS NULL AND organization_name IS NOT NULL; +------------------------ Merchant Account ----------------------- -- The new primary key for v2 merchant account will be `id` ALTER TABLE merchant_account DROP CONSTRAINT merchant_account_pkey; @@ -33,6 +35,7 @@ WHERE merchant_id IS NULL; ALTER TABLE merchant_account ADD PRIMARY KEY (merchant_id); +------------------------ Business Profile ----------------------- UPDATE business_profile SET profile_id = id WHERE profile_id IS NULL; @@ -42,6 +45,7 @@ ALTER TABLE business_profile DROP COLUMN id; ALTER TABLE business_profile ADD PRIMARY KEY (profile_id); +------------------------ Merchant Connector Account ----------------------- ALTER TABLE merchant_connector_account DROP CONSTRAINT merchant_connector_account_pkey; UPDATE merchant_connector_account @@ -54,6 +58,9 @@ ADD PRIMARY KEY (merchant_connector_id); ALTER TABLE merchant_connector_account ALTER COLUMN profile_id DROP NOT NULL; +DROP INDEX IF EXISTS merchant_connector_account_profile_id_index; + +------------------------ Customers ----------------------- -- Run this query only when V1 is deprecated ALTER TABLE customers DROP CONSTRAINT customers_pkey; @@ -65,6 +72,7 @@ WHERE customer_id IS NULL; ALTER TABLE customers ADD PRIMARY KEY (merchant_id, customer_id); +------------------------ Payment Intent ----------------------- ALTER TABLE payment_intent DROP CONSTRAINT payment_intent_pkey; UPDATE payment_intent @@ -73,3 +81,12 @@ WHERE payment_id IS NULL; ALTER TABLE payment_intent ADD PRIMARY KEY (payment_id, merchant_id); + +ALTER TABLE payment_intent +ALTER COLUMN profile_id DROP NOT NULL; + +ALTER TABLE payment_intent +ALTER COLUMN currency DROP NOT NULL; + +ALTER TABLE payment_intent +ALTER COLUMN client_secret DROP NOT NULL; diff --git a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql index 440391529ab..8d89e4bc92f 100644 --- a/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql +++ b/v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql @@ -69,6 +69,8 @@ ALTER TABLE merchant_connector_account ALTER COLUMN profile_id SET NOT NULL; +CREATE INDEX IF NOT EXISTS merchant_connector_account_profile_id_index ON merchant_connector_account (profile_id); + ------------------------ Customers ----------------------- -- Run this query only when V1 is deprecated ALTER TABLE customers DROP CONSTRAINT IF EXISTS customers_pkey; @@ -88,3 +90,12 @@ ALTER TABLE payment_intent DROP CONSTRAINT payment_intent_pkey; ALTER TABLE payment_intent ADD PRIMARY KEY (id); + +-- This migration is to make fields mandatory in payment_intent table +ALTER TABLE payment_intent +ALTER COLUMN profile_id +SET NOT NULL, + ALTER COLUMN currency +SET NOT NULL, + ALTER COLUMN client_secret +SET NOT NULL; diff --git a/v2_migrations/2024-08-28-081847_drop_v1_columns/down.sql b/v2_migrations/2024-08-28-081847_drop_v1_columns/down.sql index 83b369beac7..004c46a67d4 100644 --- a/v2_migrations/2024-08-28-081847_drop_v1_columns/down.sql +++ b/v2_migrations/2024-08-28-081847_drop_v1_columns/down.sql @@ -59,4 +59,11 @@ ADD COLUMN IF NOT EXISTS payment_id VARCHAR(64) NOT NULL, ADD COLUMN business_label VARCHAR(64), ADD COLUMN incremental_authorization_allowed BOOLEAN, ADD COLUMN fingerprint_id VARCHAR(64), - ADD COLUMN merchant_decision VARCHAR(64); + ADD COLUMN merchant_decision VARCHAR(64), + ADD COLUMN statement_descriptor_name VARCHAR(255), + ADD COLUMN amount_to_capture BIGINT, + ADD COLUMN off_session BOOLEAN, + ADD COLUMN payment_confirm_source "PaymentSource", + ADD COLUMN merchant_order_reference_id VARCHAR(255), + ADD COLUMN is_payment_processor_token_flow BOOLEAN, + ADD COLUMN charges jsonb; diff --git a/v2_migrations/2024-08-28-081847_drop_v1_columns/up.sql b/v2_migrations/2024-08-28-081847_drop_v1_columns/up.sql index 0879d667f99..0d2fcdd61d9 100644 --- a/v2_migrations/2024-08-28-081847_drop_v1_columns/up.sql +++ b/v2_migrations/2024-08-28-081847_drop_v1_columns/up.sql @@ -57,4 +57,11 @@ ALTER TABLE payment_intent DROP COLUMN payment_id, DROP COLUMN business_label, DROP COLUMN incremental_authorization_allowed, DROP COLUMN fingerprint_id, - DROP COLUMN merchant_decision; + DROP COLUMN merchant_decision, + DROP COLUMN statement_descriptor_name, + DROP COLUMN amount_to_capture, + DROP COLUMN off_session, + DROP COLUMN payment_confirm_source, + DROP COLUMN merchant_order_reference_id, + DROP COLUMN is_payment_processor_token_flow, + DROP COLUMN charges; diff --git a/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql b/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql deleted file mode 100644 index 32225e3496b..00000000000 --- a/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -DROP INDEX IF EXISTS merchant_connector_account_profile_id_index; \ No newline at end of file diff --git a/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql b/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql deleted file mode 100644 index f4ca993fef4..00000000000 --- a/v2_migrations/2024-09-13-075054_create-index-profile-id-mca/up.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Your SQL goes here -CREATE INDEX IF NOT EXISTS merchant_connector_account_profile_id_index ON merchant_connector_account (profile_id); From fe15cc79f57684b928f5413a9b928ea5b4071b92 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:56:24 +0530 Subject: [PATCH 40/48] feat(router): add support for Samsung Pay payment method (#5955) --- api-reference-v2/openapi_spec.json | 231 +++++++++++++++++- api-reference/openapi_spec.json | 231 +++++++++++++++++- config/config.example.toml | 1 + config/deployments/integration_test.toml | 1 + config/deployments/production.toml | 1 + config/deployments/sandbox.toml | 1 + config/development.toml | 1 + config/docker_compose.toml | 1 + crates/api_models/src/admin.rs | 49 ++++ crates/api_models/src/payments.rs | 110 ++++++++- crates/connector_configs/src/transformer.rs | 2 +- .../src/payment_method_data.rs | 48 +++- crates/hyperswitch_interfaces/src/errors.rs | 2 + crates/kgraph_utils/benches/evaluation.rs | 2 + crates/kgraph_utils/src/mca.rs | 2 + crates/openapi/src/openapi.rs | 8 + crates/openapi/src/openapi_v2.rs | 8 + .../src/connector/adyen/transformers.rs | 2 +- .../src/connector/cybersource/transformers.rs | 86 ++++++- crates/router/src/core/admin.rs | 28 ++- .../src/core/connector_onboarding/paypal.rs | 2 + crates/router/src/core/errors/utils.rs | 4 +- .../src/core/payments/flows/session_flow.rs | 59 +++++ crates/router/src/core/payments/helpers.rs | 113 +++++++-- .../payments/operations/payment_session.rs | 1 + crates/router/src/types/api.rs | 1 + crates/router/src/types/transformers.rs | 48 ++++ 27 files changed, 997 insertions(+), 46 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 926af8c162c..29dd0d3a96e 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -5168,6 +5168,27 @@ } } }, + "ConnectorWalletDetails": { + "type": "object", + "properties": { + "apple_pay_combined": { + "type": "object", + "description": "This field contains the Apple Pay certificates and credentials for iOS and Web Apple Pay flow", + "nullable": true + }, + "apple_pay": { + "type": "object", + "description": "This field is for our legacy Apple Pay flow that contains the Apple Pay certificates and credentials for only iOS Apple Pay flow", + "nullable": true + }, + "samsung_pay": { + "type": "object", + "description": "This field contains the Samsung Pay certificates and credentials", + "nullable": true + } + }, + "additionalProperties": false + }, "CountryAlpha2": { "type": "string", "enum": [ @@ -8901,6 +8922,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -9097,6 +9126,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -9238,6 +9275,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -9363,6 +9408,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -17950,15 +18003,164 @@ } } }, - "SamsungPayWalletData": { + "SamsungPayAmountDetails": { "type": "object", "required": [ - "token" + "option", + "currency_code", + "total" ], "properties": { - "token": { + "option": { + "$ref": "#/components/schemas/SamsungPayAmountFormat" + }, + "currency_code": { + "$ref": "#/components/schemas/Currency" + }, + "total": { + "type": "string", + "description": "The total amount of the transaction", + "example": "38.02" + } + } + }, + "SamsungPayAmountFormat": { + "type": "string", + "enum": [ + "FORMAT_TOTAL_PRICE_ONLY", + "FORMAT_TOTAL_ESTIMATED_AMOUNT" + ] + }, + "SamsungPayMerchantPaymentInformation": { + "type": "object", + "required": [ + "name", + "url", + "country_code" + ], + "properties": { + "name": { + "type": "string", + "description": "Merchant name, this will be displayed on the Samsung Pay screen" + }, + "url": { + "type": "string", + "description": "Merchant domain that process payments" + }, + "country_code": { + "$ref": "#/components/schemas/CountryAlpha2" + } + } + }, + "SamsungPayProtocolType": { + "type": "string", + "enum": [ + "PROTOCOL3DS" + ] + }, + "SamsungPaySessionTokenResponse": { + "type": "object", + "required": [ + "version", + "service_id", + "order_number", + "merchant", + "amount", + "protocol", + "allowed_brands" + ], + "properties": { + "version": { + "type": "string", + "description": "Samsung Pay API version" + }, + "service_id": { + "type": "string", + "description": "Samsung Pay service ID to which session call needs to be made" + }, + "order_number": { + "type": "string", + "description": "Order number of the transaction" + }, + "merchant": { + "$ref": "#/components/schemas/SamsungPayMerchantPaymentInformation" + }, + "amount": { + "$ref": "#/components/schemas/SamsungPayAmountDetails" + }, + "protocol": { + "$ref": "#/components/schemas/SamsungPayProtocolType" + }, + "allowed_brands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of supported card brands" + } + } + }, + "SamsungPayTokenData": { + "type": "object", + "required": [ + "version", + "data" + ], + "properties": { + "type": { + "type": "string", + "description": "3DS type used by Samsung Pay", + "nullable": true + }, + "version": { "type": "string", - "description": "The encrypted payment token from Samsung" + "description": "3DS version used by Samsung Pay" + }, + "data": { + "type": "string", + "description": "Samsung Pay encrypted payment credential data" + } + } + }, + "SamsungPayWalletCredentials": { + "type": "object", + "required": [ + "card_brand", + "card_last4digits", + "3_d_s" + ], + "properties": { + "method": { + "type": "string", + "description": "Specifies authentication method used", + "nullable": true + }, + "recurring_payment": { + "type": "boolean", + "description": "Value if credential is enabled for recurring payment", + "nullable": true + }, + "card_brand": { + "type": "string", + "description": "Brand of the payment card" + }, + "card_last4digits": { + "type": "string", + "description": "Last 4 digits of the card number" + }, + "3_d_s": { + "$ref": "#/components/schemas/SamsungPayTokenData" + } + } + }, + "SamsungPayWalletData": { + "type": "object", + "required": [ + "payment_credential" + ], + "properties": { + "payment_credential": { + "$ref": "#/components/schemas/SamsungPayWalletCredentials" } } }, @@ -18166,6 +18368,27 @@ } ] }, + { + "allOf": [ + { + "$ref": "#/components/schemas/SamsungPaySessionTokenResponse" + }, + { + "type": "object", + "required": [ + "wallet_name" + ], + "properties": { + "wallet_name": { + "type": "string", + "enum": [ + "samsung_pay" + ] + } + } + } + ] + }, { "allOf": [ { diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 2635cdb42fe..404d93f0d28 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -8597,6 +8597,27 @@ } } }, + "ConnectorWalletDetails": { + "type": "object", + "properties": { + "apple_pay_combined": { + "type": "object", + "description": "This field contains the Apple Pay certificates and credentials for iOS and Web Apple Pay flow", + "nullable": true + }, + "apple_pay": { + "type": "object", + "description": "This field is for our legacy Apple Pay flow that contains the Apple Pay certificates and credentials for only iOS Apple Pay flow", + "nullable": true + }, + "samsung_pay": { + "type": "object", + "description": "This field contains the Samsung Pay certificates and credentials", + "nullable": true + } + }, + "additionalProperties": false + }, "CountryAlpha2": { "type": "string", "enum": [ @@ -12649,6 +12670,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -12876,6 +12905,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -13043,6 +13080,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -13167,6 +13212,14 @@ } ], "nullable": true + }, + "connector_wallets_details": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorWalletDetails" + } + ], + "nullable": true } }, "additionalProperties": false @@ -22195,15 +22248,164 @@ } } }, - "SamsungPayWalletData": { + "SamsungPayAmountDetails": { "type": "object", "required": [ - "token" + "option", + "currency_code", + "total" ], "properties": { - "token": { + "option": { + "$ref": "#/components/schemas/SamsungPayAmountFormat" + }, + "currency_code": { + "$ref": "#/components/schemas/Currency" + }, + "total": { + "type": "string", + "description": "The total amount of the transaction", + "example": "38.02" + } + } + }, + "SamsungPayAmountFormat": { + "type": "string", + "enum": [ + "FORMAT_TOTAL_PRICE_ONLY", + "FORMAT_TOTAL_ESTIMATED_AMOUNT" + ] + }, + "SamsungPayMerchantPaymentInformation": { + "type": "object", + "required": [ + "name", + "url", + "country_code" + ], + "properties": { + "name": { + "type": "string", + "description": "Merchant name, this will be displayed on the Samsung Pay screen" + }, + "url": { + "type": "string", + "description": "Merchant domain that process payments" + }, + "country_code": { + "$ref": "#/components/schemas/CountryAlpha2" + } + } + }, + "SamsungPayProtocolType": { + "type": "string", + "enum": [ + "PROTOCOL3DS" + ] + }, + "SamsungPaySessionTokenResponse": { + "type": "object", + "required": [ + "version", + "service_id", + "order_number", + "merchant", + "amount", + "protocol", + "allowed_brands" + ], + "properties": { + "version": { + "type": "string", + "description": "Samsung Pay API version" + }, + "service_id": { + "type": "string", + "description": "Samsung Pay service ID to which session call needs to be made" + }, + "order_number": { + "type": "string", + "description": "Order number of the transaction" + }, + "merchant": { + "$ref": "#/components/schemas/SamsungPayMerchantPaymentInformation" + }, + "amount": { + "$ref": "#/components/schemas/SamsungPayAmountDetails" + }, + "protocol": { + "$ref": "#/components/schemas/SamsungPayProtocolType" + }, + "allowed_brands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of supported card brands" + } + } + }, + "SamsungPayTokenData": { + "type": "object", + "required": [ + "version", + "data" + ], + "properties": { + "type": { + "type": "string", + "description": "3DS type used by Samsung Pay", + "nullable": true + }, + "version": { "type": "string", - "description": "The encrypted payment token from Samsung" + "description": "3DS version used by Samsung Pay" + }, + "data": { + "type": "string", + "description": "Samsung Pay encrypted payment credential data" + } + } + }, + "SamsungPayWalletCredentials": { + "type": "object", + "required": [ + "card_brand", + "card_last4digits", + "3_d_s" + ], + "properties": { + "method": { + "type": "string", + "description": "Specifies authentication method used", + "nullable": true + }, + "recurring_payment": { + "type": "boolean", + "description": "Value if credential is enabled for recurring payment", + "nullable": true + }, + "card_brand": { + "type": "string", + "description": "Brand of the payment card" + }, + "card_last4digits": { + "type": "string", + "description": "Last 4 digits of the card number" + }, + "3_d_s": { + "$ref": "#/components/schemas/SamsungPayTokenData" + } + } + }, + "SamsungPayWalletData": { + "type": "object", + "required": [ + "payment_credential" + ], + "properties": { + "payment_credential": { + "$ref": "#/components/schemas/SamsungPayWalletCredentials" } } }, @@ -22411,6 +22613,27 @@ } ] }, + { + "allOf": [ + { + "$ref": "#/components/schemas/SamsungPaySessionTokenResponse" + }, + { + "type": "object", + "required": [ + "wallet_name" + ], + "properties": { + "wallet_name": { + "type": "string", + "enum": [ + "samsung_pay" + ] + } + } + } + ] + }, { "allOf": [ { diff --git a/config/config.example.toml b/config/config.example.toml index a6af984b01f..861785dc34c 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -534,6 +534,7 @@ credit = { currency = "USD,GBP,EUR" } debit = { currency = "USD,GBP,EUR" } apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } +samsung_pay = { currency = "USD,GBP,EUR" } [pm_filters.stax] credit = { currency = "USD" } diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index abb50a2c2f0..3a54c5ef08f 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -304,6 +304,7 @@ credit = { currency = "USD,GBP,EUR" } debit = { currency = "USD,GBP,EUR" } apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } +samsung_pay = { currency = "USD,GBP,EUR" } [pm_filters.volt] open_banking_uk = {country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,MT,NL,PT,SI,SK,BG,CZ,DK,HU,NO,PL,RO,SE,AU,BR", currency = "EUR,GBP,DKK,NOK,PLN,SEK,AUD,BRL"} diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 38799c23858..ec3737a5c64 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -278,6 +278,7 @@ credit = { currency = "USD,GBP,EUR" } debit = { currency = "USD,GBP,EUR" } apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } +samsung_pay = { currency = "USD,GBP,EUR" } [pm_filters.braintree] paypal.currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 2217a72429c..1c605ef35ac 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -281,6 +281,7 @@ credit = { currency = "USD,GBP,EUR" } debit = { currency = "USD,GBP,EUR" } apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } +samsung_pay = { currency = "USD,GBP,EUR" } [pm_filters.braintree] paypal.currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" diff --git a/config/development.toml b/config/development.toml index 43130cb78f2..6e56d5bb14d 100644 --- a/config/development.toml +++ b/config/development.toml @@ -449,6 +449,7 @@ credit = { currency = "USD,GBP,EUR" } debit = { currency = "USD,GBP,EUR" } apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } +samsung_pay = { currency = "USD,GBP,EUR" } [pm_filters.braintree] paypal = { currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" } diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 2c6b12a88a0..15038da0125 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -414,6 +414,7 @@ credit = { currency = "USD,GBP,EUR" } debit = { currency = "USD,GBP,EUR" } apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } +samsung_pay = { currency = "USD,GBP,EUR" } [pm_filters.helcim] credit = { currency = "USD" } diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 5a9beb565d4..6f656339569 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -745,6 +745,10 @@ pub struct MerchantConnectorCreate { /// In case the merchant needs to store any additional sensitive data #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, } #[cfg(feature = "v2")] @@ -882,6 +886,10 @@ pub struct MerchantConnectorCreate { /// In case the merchant needs to store any additional sensitive data #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, } #[cfg(feature = "v1")] @@ -1102,6 +1110,10 @@ pub struct MerchantConnectorResponse { #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, } #[cfg(feature = "v2")] @@ -1221,6 +1233,10 @@ pub struct MerchantConnectorResponse { #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, } #[cfg(feature = "v1")] @@ -1327,6 +1343,10 @@ pub struct MerchantConnectorListResponse { #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, } #[cfg(feature = "v1")] @@ -1417,6 +1437,10 @@ pub struct MerchantConnectorListResponse { #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, } #[cfg(feature = "v2")] @@ -1512,6 +1536,28 @@ pub struct MerchantConnectorUpdate { /// In case the merchant needs to store any additional sensitive data #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + #[schema(value_type = Option)] + pub connector_wallets_details: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(deny_unknown_fields)] + +pub struct ConnectorWalletDetails { + /// This field contains the Apple Pay certificates and credentials for iOS and Web Apple Pay flow + #[serde(skip_serializing_if = "Option::is_none")] + #[schema(value_type = Option)] + pub apple_pay_combined: Option, + /// This field is for our legacy Apple Pay flow that contains the Apple Pay certificates and credentials for only iOS Apple Pay flow + #[serde(skip_serializing_if = "Option::is_none")] + #[schema(value_type = Option)] + pub apple_pay: Option, + /// This field contains the Samsung Pay certificates and credentials + #[serde(skip_serializing_if = "Option::is_none")] + #[schema(value_type = Option)] + pub samsung_pay: Option, } /// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc." @@ -1597,6 +1643,9 @@ pub struct MerchantConnectorUpdate { /// In case the merchant needs to store any additional sensitive data #[schema(value_type = Option)] pub additional_merchant_data: Option, + + /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials + pub connector_wallets_details: Option, } #[cfg(feature = "v2")] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index d1b658dab4e..0236ee0c51a 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2637,9 +2637,37 @@ impl GetAddressFromPaymentMethodData for WalletData { #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[serde(rename_all = "snake_case")] pub struct SamsungPayWalletData { - /// The encrypted payment token from Samsung + pub payment_credential: SamsungPayWalletCredentials, +} + +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub struct SamsungPayWalletCredentials { + /// Specifies authentication method used + pub method: Option, + /// Value if credential is enabled for recurring payment + pub recurring_payment: Option, + /// Brand of the payment card + pub card_brand: String, + /// Last 4 digits of the card number + #[serde(rename = "card_last4digits")] + pub card_last_four_digits: String, + /// Samsung Pay token data + #[serde(rename = "3_d_s")] + pub token_data: SamsungPayTokenData, +} + +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub struct SamsungPayTokenData { + /// 3DS type used by Samsung Pay + #[serde(rename = "type")] + pub three_ds_type: Option, + /// 3DS version used by Samsung Pay + pub version: String, + /// Samsung Pay encrypted payment credential data #[schema(value_type = String)] - pub token: Secret, + pub data: Secret, } #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] @@ -4624,6 +4652,20 @@ pub struct GpaySessionTokenData { pub data: GpayMetaData, } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct SamsungPaySessionTokenData { + #[serde(rename = "samsung_pay")] + pub data: SamsungPayMetadata, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct SamsungPayMetadata { + pub service_id: String, + pub merchant_display_name: String, + pub merchant_business_country: api_enums::CountryAlpha2, + pub allowed_brands: Vec, +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PaypalSdkMetaData { pub client_id: String, @@ -4789,6 +4831,8 @@ pub struct SessionTokenForSimplifiedApplePay { pub enum SessionToken { /// The session response structure for Google Pay GooglePay(Box), + /// The session response structure for Samsung Pay + SamsungPay(Box), /// The session response structure for Klarna Klarna(Box), /// The session response structure for PayPal @@ -4846,6 +4890,68 @@ pub struct GooglePaySessionResponse { pub secrets: Option, } +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub struct SamsungPaySessionTokenResponse { + /// Samsung Pay API version + pub version: String, + /// Samsung Pay service ID to which session call needs to be made + pub service_id: String, + /// Order number of the transaction + pub order_number: String, + /// Field containing merchant information + #[serde(rename = "merchant")] + pub merchant_payment_information: SamsungPayMerchantPaymentInformation, + /// Field containing the payment amount + pub amount: SamsungPayAmountDetails, + /// Payment protocol type + pub protocol: SamsungPayProtocolType, + /// List of supported card brands + pub allowed_brands: Vec, +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum SamsungPayProtocolType { + Protocol3ds, +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub struct SamsungPayMerchantPaymentInformation { + /// Merchant name, this will be displayed on the Samsung Pay screen + pub name: String, + /// Merchant domain that process payments + pub url: String, + /// Merchant country code + #[schema(value_type = CountryAlpha2, example = "US")] + pub country_code: api_enums::CountryAlpha2, +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub struct SamsungPayAmountDetails { + #[serde(rename = "option")] + /// Amount format to be displayed + pub amount_format: SamsungPayAmountFormat, + /// The currency code + #[schema(value_type = Currency, example = "USD")] + pub currency_code: api_enums::Currency, + /// The total amount of the transaction + #[serde(rename = "total")] + #[schema(value_type = String, example = "38.02")] + pub total_amount: StringMajorUnit, +} + +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum SamsungPayAmountFormat { + /// Display the total amount only + FormatTotalPriceOnly, + /// Display "Total (Estimated amount)" and total amount + FormatTotalEstimatedAmount, +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] #[serde(rename_all = "lowercase")] pub struct GpayShippingAddressParameters { diff --git a/crates/connector_configs/src/transformer.rs b/crates/connector_configs/src/transformer.rs index fe39da67a93..68ade2909b4 100644 --- a/crates/connector_configs/src/transformer.rs +++ b/crates/connector_configs/src/transformer.rs @@ -56,7 +56,7 @@ impl DashboardRequestPayload { | (Connector::Stripe, WeChatPay) => { Some(api_models::enums::PaymentExperience::DisplayQrCode) } - (_, GooglePay) | (_, ApplePay) => { + (_, GooglePay) | (_, ApplePay) | (_, PaymentMethodType::SamsungPay) => { Some(api_models::enums::PaymentExperience::InvokeSdkClient) } _ => Some(api_models::enums::PaymentExperience::RedirectToUrl), diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index b21bedd880c..9f505f26712 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -135,10 +135,30 @@ pub struct MifinityData { } #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] - +#[serde(rename_all = "snake_case")] pub struct SamsungPayWalletData { - /// The encrypted payment token from Samsung - pub token: Secret, + pub payment_credential: SamsungPayWalletCredentials, +} + +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub struct SamsungPayWalletCredentials { + pub method: Option, + pub recurring_payment: Option, + pub card_brand: String, + #[serde(rename = "card_last4digits")] + pub card_last_four_digits: String, + #[serde(rename = "3_d_s")] + pub token_data: SamsungPayTokenData, +} + +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub struct SamsungPayTokenData { + #[serde(rename = "type")] + pub three_ds_type: Option, + pub version: String, + pub data: Secret, } #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -670,9 +690,7 @@ impl From for WalletData { }) } api_models::payments::WalletData::SamsungPay(samsung_pay_data) => { - Self::SamsungPay(Box::new(SamsungPayWalletData { - token: samsung_pay_data.token, - })) + Self::SamsungPay(Box::new(SamsungPayWalletData::from(samsung_pay_data))) } api_models::payments::WalletData::TwintRedirect {} => Self::TwintRedirect {}, api_models::payments::WalletData::VippsRedirect {} => Self::VippsRedirect {}, @@ -736,6 +754,24 @@ impl From for ApplePayWalletData { } } +impl From> for SamsungPayWalletData { + fn from(value: Box) -> Self { + Self { + payment_credential: SamsungPayWalletCredentials { + method: value.payment_credential.method, + recurring_payment: value.payment_credential.recurring_payment, + card_brand: value.payment_credential.card_brand, + card_last_four_digits: value.payment_credential.card_last_four_digits, + token_data: SamsungPayTokenData { + three_ds_type: value.payment_credential.token_data.three_ds_type, + version: value.payment_credential.token_data.version, + data: value.payment_credential.token_data.data, + }, + }, + } + } +} + impl From for PayLaterData { fn from(value: api_models::payments::PayLaterData) -> Self { match value { diff --git a/crates/hyperswitch_interfaces/src/errors.rs b/crates/hyperswitch_interfaces/src/errors.rs index 06f197ebf0b..c6009557341 100644 --- a/crates/hyperswitch_interfaces/src/errors.rs +++ b/crates/hyperswitch_interfaces/src/errors.rs @@ -41,6 +41,8 @@ pub enum ConnectorError { FailedToObtainCertificate, #[error("Connector meta data not found")] NoConnectorMetaData, + #[error("Connector wallet details not found")] + NoConnectorWalletDetails, #[error("Failed to obtain certificate key")] FailedToObtainCertificateKey, #[error("This step has not been implemented for: {0}")] diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index b98b854fba8..858c013f41d 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -71,6 +71,7 @@ fn build_test_data( pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, additional_merchant_data: None, + connector_wallets_details: None, }; #[cfg(feature = "v1")] @@ -95,6 +96,7 @@ fn build_test_data( pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, additional_merchant_data: None, + connector_wallets_details: None, }; let config = CountryCurrencyFilter { connector_configs: HashMap::new(), diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 7d04f5d4238..2ec0cbe8031 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -759,6 +759,7 @@ mod tests { pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, additional_merchant_data: None, + connector_wallets_details: None, }; #[cfg(feature = "v1")] let stripe_account = MerchantConnectorResponse { @@ -818,6 +819,7 @@ mod tests { pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, additional_merchant_data: None, + connector_wallets_details: None, }; let config_map = kgraph_types::CountryCurrencyFilter { diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index f4cceec8bec..85865584494 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -273,6 +273,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::enums::UIWidgetFormLayout, api_models::admin::MerchantConnectorCreate, api_models::admin::AdditionalMerchantData, + api_models::admin::ConnectorWalletDetails, api_models::admin::MerchantRecipientData, api_models::admin::MerchantAccountData, api_models::admin::MerchantConnectorUpdate, @@ -410,6 +411,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::GpayTokenizationData, api_models::payments::GooglePayPaymentMethodInfo, api_models::payments::ApplePayWalletData, + api_models::payments::SamsungPayWalletCredentials, + api_models::payments::SamsungPayTokenData, api_models::payments::ApplepayPaymentMethod, api_models::payments::PaymentsCancelRequest, api_models::payments::PaymentListConstraints, @@ -431,6 +434,11 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::GooglePayRedirectData, api_models::payments::GooglePayThirdPartySdk, api_models::payments::GooglePaySessionResponse, + api_models::payments::SamsungPaySessionTokenResponse, + api_models::payments::SamsungPayMerchantPaymentInformation, + api_models::payments::SamsungPayAmountDetails, + api_models::payments::SamsungPayAmountFormat, + api_models::payments::SamsungPayProtocolType, api_models::payments::GpayShippingAddressParameters, api_models::payments::GpayBillingAddressParameters, api_models::payments::GpayBillingAddressFormat, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index da0a95aa6de..05fded520f0 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -199,6 +199,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::enums::UIWidgetFormLayout, api_models::admin::MerchantConnectorCreate, api_models::admin::AdditionalMerchantData, + api_models::admin::ConnectorWalletDetails, api_models::admin::MerchantRecipientData, api_models::admin::MerchantAccountData, api_models::admin::MerchantConnectorUpdate, @@ -337,6 +338,13 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::GooglePayPaymentMethodInfo, api_models::payments::ApplePayWalletData, api_models::payments::ApplepayPaymentMethod, + api_models::payments::SamsungPaySessionTokenResponse, + api_models::payments::SamsungPayMerchantPaymentInformation, + api_models::payments::SamsungPayAmountDetails, + api_models::payments::SamsungPayAmountFormat, + api_models::payments::SamsungPayProtocolType, + api_models::payments::SamsungPayWalletCredentials, + api_models::payments::SamsungPayTokenData, api_models::payments::PaymentsCancelRequest, api_models::payments::PaymentListConstraints, api_models::payments::PaymentListResponse, diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 361ebece299..dfb0963c70a 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2083,7 +2083,7 @@ impl<'a> TryFrom<(&domain::WalletData, &types::PaymentsAuthorizeRouterData)> domain::WalletData::SamsungPay(samsung_data) => { let data = SamsungPayPmData { payment_type: PaymentType::Samsungpay, - samsung_pay_token: samsung_data.token.to_owned(), + samsung_pay_token: samsung_data.payment_credential.token_data.data.to_owned(), }; Ok(AdyenPaymentMethod::SamsungPay(Box::new(data))) } diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 9ad4f0604a4..9c596f5575c 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -436,12 +436,27 @@ pub struct FluidData { pub const FLUID_DATA_DESCRIPTOR: &str = "RklEPUNPTU1PTi5BUFBMRS5JTkFQUC5QQVlNRU5U"; +pub const FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY: &str = "FID=COMMON.SAMSUNG.INAPP.PAYMENT"; + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct GooglePayPaymentInformation { fluid_data: FluidData, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SamsungPayTokenizedCard { + transaction_type: TransactionType, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SamsungPayPaymentInformation { + fluid_data: FluidData, + tokenized_card: SamsungPayTokenizedCard, +} + #[derive(Debug, Serialize)] #[serde(untagged)] pub enum PaymentInformation { @@ -450,6 +465,7 @@ pub enum PaymentInformation { ApplePay(Box), ApplePayToken(Box), MandatePayment(Box), + SamsungPay(Box), NetworkToken(Box), } @@ -504,12 +520,15 @@ pub struct AdditionalAmount { pub enum PaymentSolution { ApplePay, GooglePay, + SamsungPay, } #[derive(Debug, Serialize)] pub enum TransactionType { #[serde(rename = "1")] ApplePay, + #[serde(rename = "1")] + SamsungPay, } impl From for String { @@ -517,6 +536,7 @@ impl From for String { let payment_solution = match solution { PaymentSolution::ApplePay => "001", PaymentSolution::GooglePay => "012", + PaymentSolution::SamsungPay => "008", }; payment_solution.to_string() } @@ -575,7 +595,7 @@ impl let mut commerce_indicator = solution .as_ref() .map(|pm_solution| match pm_solution { - PaymentSolution::ApplePay => network + PaymentSolution::ApplePay | PaymentSolution::SamsungPay => network .as_ref() .map(|card_network| match card_network.to_lowercase().as_str() { "amex" => "aesk", @@ -1491,6 +1511,66 @@ impl } } +impl + TryFrom<( + &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, + Box, + )> for CybersourcePaymentsRequest +{ + type Error = error_stack::Report; + fn try_from( + (item, samsung_pay_data): ( + &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, + Box, + ), + ) -> Result { + let email = item + .router_data + .get_billing_email() + .or(item.router_data.request.get_email())?; + let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?; + let order_information = OrderInformationWithBill::from((item, Some(bill_to))); + + let payment_information = + PaymentInformation::SamsungPay(Box::new(SamsungPayPaymentInformation { + fluid_data: FluidData { + value: Secret::from( + consts::BASE64_ENGINE + .encode(samsung_pay_data.payment_credential.token_data.data.peek()), + ), + descriptor: Some( + consts::BASE64_ENGINE.encode(FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY), + ), + }, + tokenized_card: SamsungPayTokenizedCard { + transaction_type: TransactionType::SamsungPay, + }, + })); + + let processing_information = ProcessingInformation::try_from(( + item, + Some(PaymentSolution::SamsungPay), + Some(samsung_pay_data.payment_credential.card_brand), + ))?; + let client_reference_information = ClientReferenceInformation::from(item); + let merchant_defined_information = item + .router_data + .request + .metadata + .clone() + .map(Vec::::foreign_from); + + Ok(Self { + processing_information, + payment_information, + order_information, + client_reference_information, + consumer_authentication_information: None, + merchant_defined_information, + }) + } +} + impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> for CybersourcePaymentsRequest { @@ -1588,6 +1668,9 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> domain::WalletData::GooglePay(google_pay_data) => { Self::try_from((item, google_pay_data)) } + domain::WalletData::SamsungPay(samsung_pay_data) => { + Self::try_from((item, samsung_pay_data)) + } domain::WalletData::AliPayQr(_) | domain::WalletData::AliPayRedirect(_) | domain::WalletData::AliPayHkRedirect(_) @@ -1604,7 +1687,6 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::WalletData::MobilePayRedirect(_) | domain::WalletData::PaypalRedirect(_) | domain::WalletData::PaypalSdk(_) - | domain::WalletData::SamsungPay(_) | domain::WalletData::TwintRedirect {} | domain::WalletData::VippsRedirect {} | domain::WalletData::TouchNGoRedirect(_) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 65bdeb04a4a..ad9925a241f 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -2129,10 +2129,14 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect } else { None }, - connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( - state, &key_store, &metadata, - ) - .await?, + connector_wallets_details: + helpers::get_encrypted_connector_wallets_details_with_apple_pay_certificates( + state, + &key_store, + &metadata, + &self.connector_wallets_details, + ) + .await?, }) } } @@ -2306,10 +2310,14 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect } else { None }, - connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( - state, &key_store, &metadata, - ) - .await?, + connector_wallets_details: + helpers::get_encrypted_connector_wallets_details_with_apple_pay_certificates( + state, + &key_store, + &metadata, + &self.connector_wallets_details, + ) + .await?, }) } } @@ -2440,7 +2448,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { applepay_verified_domains: None, pm_auth_config: self.pm_auth_config.clone(), status: connector_status, - connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(state, &key_store, &self.metadata).await?, + connector_wallets_details: helpers::get_encrypted_connector_wallets_details_with_apple_pay_certificates(state, &key_store, &self.metadata, &self.connector_wallets_details).await?, additional_merchant_data: if let Some(mcd) = merchant_recipient_data { Some(domain_types::crypto_operation( key_manager_state, @@ -2605,7 +2613,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { applepay_verified_domains: None, pm_auth_config: self.pm_auth_config.clone(), status: connector_status, - connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(state, &key_store, &self.metadata).await?, + connector_wallets_details: helpers::get_encrypted_connector_wallets_details_with_apple_pay_certificates(state, &key_store, &self.metadata, &self.connector_wallets_details).await?, test_mode: self.test_mode, business_country: self.business_country, business_label: self.business_label.clone(), diff --git a/crates/router/src/core/connector_onboarding/paypal.rs b/crates/router/src/core/connector_onboarding/paypal.rs index 0dca02bf79d..88e1eb80618 100644 --- a/crates/router/src/core/connector_onboarding/paypal.rs +++ b/crates/router/src/core/connector_onboarding/paypal.rs @@ -162,6 +162,7 @@ pub async fn update_mca( pm_auth_config: None, test_mode: None, additional_merchant_data: None, + connector_wallets_details: None, }; #[cfg(feature = "v2")] let request = MerchantConnectorUpdate { @@ -177,6 +178,7 @@ pub async fn update_mca( pm_auth_config: None, merchant_id: merchant_id.clone(), additional_merchant_data: None, + connector_wallets_details: None, }; let mca_response = admin::update_connector(state.clone(), &merchant_id, None, &connector_id, request).await?; diff --git a/crates/router/src/core/errors/utils.rs b/crates/router/src/core/errors/utils.rs index 549857494f0..70eaa04cacc 100644 --- a/crates/router/src/core/errors/utils.rs +++ b/crates/router/src/core/errors/utils.rs @@ -185,6 +185,7 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::FailedToObtainAuthType | errors::ConnectorError::FailedToObtainCertificate | errors::ConnectorError::NoConnectorMetaData + | errors::ConnectorError::NoConnectorWalletDetails | errors::ConnectorError::FailedToObtainCertificateKey | errors::ConnectorError::FlowNotSupported { .. } | errors::ConnectorError::CaptureMethodNotSupported @@ -285,7 +286,7 @@ impl ConnectorErrorExt for error_stack::Result errors::ConnectorError::InvalidWallet | errors::ConnectorError::ResponseHandlingFailed | errors::ConnectorError::FailedToObtainCertificate | - errors::ConnectorError::NoConnectorMetaData | + errors::ConnectorError::NoConnectorMetaData | errors::ConnectorError::NoConnectorWalletDetails | errors::ConnectorError::FailedToObtainCertificateKey | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID | @@ -372,6 +373,7 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::FailedToObtainAuthType | errors::ConnectorError::FailedToObtainCertificate | errors::ConnectorError::NoConnectorMetaData + | errors::ConnectorError::NoConnectorWalletDetails | errors::ConnectorError::FailedToObtainCertificateKey | errors::ConnectorError::NotImplemented(_) | errors::ConnectorError::NotSupported { .. } diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 9dfd4482885..b65ee66474b 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -496,6 +496,62 @@ async fn create_applepay_session_token( } } +fn create_samsung_pay_session_token( + router_data: &types::PaymentsSessionRouterData, + header_payload: api_models::payments::HeaderPayload, +) -> RouterResult { + let samsung_pay_wallet_details = router_data + .connector_wallets_details + .clone() + .parse_value::("SamsungPaySessionTokenData") + .change_context(errors::ConnectorError::NoConnectorWalletDetails) + .change_context(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "connector_wallets_details".to_string(), + expected_format: "samsung_pay_metadata_format".to_string(), + })?; + + let required_amount_type = StringMajorUnitForConnector; + let samsung_pay_amount = required_amount_type + .convert( + router_data.request.minor_amount, + router_data.request.currency, + ) + .change_context(errors::ApiErrorResponse::PreconditionFailed { + message: "Failed to convert amount to string major unit for Samsung Pay".to_string(), + })?; + + let merchant_domain = header_payload + .x_merchant_domain + .get_required_value("samsung pay domain") + .attach_printable("Failed to get domain for samsung pay session call")?; + + Ok(types::PaymentsSessionRouterData { + response: Ok(types::PaymentsResponseData::SessionResponse { + session_token: payment_types::SessionToken::SamsungPay(Box::new( + payment_types::SamsungPaySessionTokenResponse { + version: "2".to_string(), + service_id: samsung_pay_wallet_details.data.service_id, + order_number: router_data.payment_id.clone(), + merchant_payment_information: + payment_types::SamsungPayMerchantPaymentInformation { + name: samsung_pay_wallet_details.data.merchant_display_name, + url: merchant_domain, + country_code: samsung_pay_wallet_details.data.merchant_business_country, + }, + amount: payment_types::SamsungPayAmountDetails { + amount_format: payment_types::SamsungPayAmountFormat::FormatTotalPriceOnly, + currency_code: router_data.request.currency, + total_amount: samsung_pay_amount, + }, + protocol: payment_types::SamsungPayProtocolType::Protocol3ds, + allowed_brands: samsung_pay_wallet_details.data.allowed_brands, + }, + )), + }), + ..router_data.clone() + }) +} + fn get_session_request_for_simplified_apple_pay( apple_pay_merchant_identifier: String, session_token_data: payment_types::SessionTokenForSimplifiedApplePay, @@ -881,6 +937,9 @@ impl RouterDataSession for types::PaymentsSessionRouterData { api::GetToken::GpayMetadata => { create_gpay_session_token(state, self, connector, business_profile) } + api::GetToken::SamsungPayMetadata => { + create_samsung_pay_session_token(self, header_payload) + } api::GetToken::ApplePayMetadata => { create_applepay_session_token( state, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index eb512399233..b0e2f61d184 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -4524,30 +4524,34 @@ pub fn is_apple_pay_simplified_flow( )) } -pub async fn get_encrypted_apple_pay_connector_wallets_details( +// This function will return the encrypted connector wallets details with Apple Pay certificates +// Currently apple pay certifiactes are stored in the metadata which is not encrypted. +// In future we want those certificates to be encrypted and stored in the connector_wallets_details. +// As part of migration fallback this function checks apple pay details are present in connector_wallets_details +// If yes, it will encrypt connector_wallets_details and store it in the database. +// If no, it will check if apple pay details are present in metadata and merge it with connector_wallets_details, encrypt and store it. +pub async fn get_encrypted_connector_wallets_details_with_apple_pay_certificates( state: &SessionState, key_store: &domain::MerchantKeyStore, connector_metadata: &Option>, + connector_wallets_details_optional: &Option, ) -> RouterResult>>> { - let apple_pay_metadata = get_applepay_metadata(connector_metadata.clone()) - .map_err(|error| { - logger::error!( - "Apple pay metadata parsing failed in get_encrypted_apple_pay_connector_wallets_details {:?}", - error - ) - }) - .ok(); + let connector_wallet_details_with_apple_pay_metadata_optional = + get_apple_pay_metadata_if_needed(connector_metadata, connector_wallets_details_optional) + .await?; - let connector_apple_pay_details = apple_pay_metadata - .map(|metadata| { - serde_json::to_value(metadata) + let connector_wallets_details = connector_wallet_details_with_apple_pay_metadata_optional + .map(|details| { + serde_json::to_value(details) .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to serialize apple pay metadata as JSON") + .attach_printable("Failed to serialize Apple Pay metadata as JSON") }) .transpose()? .map(masking::Secret::new); + let key_manager_state: KeyManagerState = state.into(); - let encrypted_connector_apple_pay_details = connector_apple_pay_details + let encrypted_connector_wallets_details = connector_wallets_details + .clone() .async_lift(|wallets_details| async { types::crypto_operation( &key_manager_state, @@ -4562,7 +4566,86 @@ pub async fn get_encrypted_apple_pay_connector_wallets_details( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encrypting connector wallets details")?; - Ok(encrypted_connector_apple_pay_details) + + Ok(encrypted_connector_wallets_details) +} + +async fn get_apple_pay_metadata_if_needed( + connector_metadata: &Option>, + connector_wallets_details_optional: &Option, +) -> RouterResult> { + if let Some(connector_wallets_details) = connector_wallets_details_optional { + if connector_wallets_details.apple_pay_combined.is_some() + || connector_wallets_details.apple_pay.is_some() + { + return Ok(Some(connector_wallets_details.clone())); + } + // Otherwise, merge Apple Pay metadata + return get_and_merge_apple_pay_metadata( + connector_metadata.clone(), + Some(connector_wallets_details.clone()), + ) + .await; + } + + // If connector_wallets_details_optional is None, attempt to get Apple Pay metadata + get_and_merge_apple_pay_metadata(connector_metadata.clone(), None).await +} + +async fn get_and_merge_apple_pay_metadata( + connector_metadata: Option>, + connector_wallets_details_optional: Option, +) -> RouterResult> { + let apple_pay_metadata_optional = get_applepay_metadata(connector_metadata) + .map_err(|error| { + logger::error!( + "Apple Pay metadata parsing failed in get_encrypted_connector_wallets_details_with_apple_pay_certificates {:?}", + error + ); + }) + .ok(); + + if let Some(apple_pay_metadata) = apple_pay_metadata_optional { + let updated_wallet_details = match apple_pay_metadata { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + apple_pay_combined_metadata, + ) => { + let combined_metadata_json = serde_json::to_value(apple_pay_combined_metadata) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to serialize Apple Pay combined metadata as JSON")?; + + api_models::admin::ConnectorWalletDetails { + apple_pay_combined: Some(masking::Secret::new(combined_metadata_json)), + apple_pay: connector_wallets_details_optional + .as_ref() + .and_then(|d| d.apple_pay.clone()), + samsung_pay: connector_wallets_details_optional + .as_ref() + .and_then(|d| d.samsung_pay.clone()), + } + } + api_models::payments::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => { + let metadata_json = serde_json::to_value(apple_pay_metadata) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to serialize Apple Pay metadata as JSON")?; + + api_models::admin::ConnectorWalletDetails { + apple_pay: Some(masking::Secret::new(metadata_json)), + apple_pay_combined: connector_wallets_details_optional + .as_ref() + .and_then(|d| d.apple_pay_combined.clone()), + samsung_pay: connector_wallets_details_optional + .as_ref() + .and_then(|d| d.samsung_pay.clone()), + } + } + }; + + return Ok(Some(updated_wallet_details)); + } + + // Return connector_wallets_details if no Apple Pay metadata was found + Ok(connector_wallets_details_optional) } pub fn get_applepay_metadata( diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 0542c279a29..cc8b839cc80 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -490,6 +490,7 @@ impl From for api::GetToken { match value { api_models::enums::PaymentMethodType::GooglePay => Self::GpayMetadata, api_models::enums::PaymentMethodType::ApplePay => Self::ApplePayMetadata, + api_models::enums::PaymentMethodType::SamsungPay => Self::SamsungPayMetadata, api_models::enums::PaymentMethodType::Paypal => Self::PaypalSdkMetadata, _ => Self::Connector, } diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 34dea537b2d..dd5e47b1005 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -190,6 +190,7 @@ pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>; #[derive(Clone, Eq, PartialEq, Debug)] pub enum GetToken { GpayMetadata, + SamsungPayMetadata, ApplePayMetadata, PaypalSdkMetadata, Connector, diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index e9a70cda9c5..bfad15e5d9d 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1068,6 +1068,18 @@ impl ForeignTryFrom }) .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()?, }; #[cfg(feature = "v2")] let response = Self { @@ -1096,6 +1108,18 @@ impl ForeignTryFrom }) .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) } @@ -1191,6 +1215,18 @@ impl ForeignTryFrom }) .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()?, }; #[cfg(feature = "v1")] let response = Self { @@ -1235,6 +1271,18 @@ impl ForeignTryFrom }) .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) } From c0cac8d9135b14945ce5763327ec16b1578ca2a2 Mon Sep 17 00:00:00 2001 From: Amisha Prabhat <55580080+Aprabhat19@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:56:38 +0530 Subject: [PATCH 41/48] fix(external_services): add proto build under the dynamic_routing feature flag (#5945) --- .github/workflows/CI-pr.yml | 6 ++++++ .github/workflows/CI-push.yml | 6 ++++++ .../workflows/postman-collection-runner.yml | 2 ++ config/config.example.toml | 4 ++++ config/deployments/env_specific.toml | 4 ++++ crates/external_services/Cargo.toml | 5 +++-- crates/external_services/build.rs | 20 +++++++++---------- .../src/grpc_client/dynamic_routing.rs | 20 +++++++++---------- 8 files changed, 44 insertions(+), 23 deletions(-) diff --git a/.github/workflows/CI-pr.yml b/.github/workflows/CI-pr.yml index c5b005849ae..e43af03ec6e 100644 --- a/.github/workflows/CI-pr.yml +++ b/.github/workflows/CI-pr.yml @@ -123,6 +123,8 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install sccache uses: taiki-e/install-action@v2.33.28 @@ -224,6 +226,8 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install sccache uses: taiki-e/install-action@v2.33.28 @@ -306,6 +310,8 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install rust cache uses: Swatinem/rust-cache@v2.7.0 diff --git a/.github/workflows/CI-push.yml b/.github/workflows/CI-push.yml index dc7195714bd..7bc55995888 100644 --- a/.github/workflows/CI-push.yml +++ b/.github/workflows/CI-push.yml @@ -73,6 +73,8 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: Swatinem/rust-cache@v2.7.0 with: @@ -155,6 +157,8 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install cargo-hack uses: baptiste0928/cargo-install@v2.2.0 @@ -225,6 +229,8 @@ jobs: - name: Install Protoc uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install rust cache uses: Swatinem/rust-cache@v2.7.0 diff --git a/.github/workflows/postman-collection-runner.yml b/.github/workflows/postman-collection-runner.yml index 95949593fbf..de8235c14ea 100644 --- a/.github/workflows/postman-collection-runner.yml +++ b/.github/workflows/postman-collection-runner.yml @@ -97,6 +97,8 @@ jobs: - name: Install Protoc if: ${{ ((github.event_name == 'pull_request') && (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name)) || (github.event_name == 'merge_group')}} uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Install Diesel CLI with Postgres Support if: ${{ ((github.event_name == 'pull_request') && (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name)) || (github.event_name == 'merge_group')}} diff --git a/config/config.example.toml b/config/config.example.toml index 861785dc34c..1f32481fec7 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -749,3 +749,7 @@ check_token_status_url= "" # base url to check token status from token servic [network_tokenization_supported_connectors] connector_list = "cybersource" # Supported connectors for network tokenization + +[grpc_client.dynamic_routing_client] # Dynamic Routing Client Configuration +host = "localhost" # Client Host +port = 7000 # Client Port diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index 62350376271..319cdafed1d 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -311,3 +311,7 @@ private_key= "" # private key to decrypt response payload from to key_id= "" # key id to encrypt data for token service delete_token_url= "" # base url to delete token from token service check_token_status_url= "" # base url to check token status from token service + +[grpc_client.dynamic_routing_client] # Dynamic Routing Client Configuration +host = "localhost" # Client Host +port = 7000 # Client Port diff --git a/crates/external_services/Cargo.toml b/crates/external_services/Cargo.toml index a882cdc9f51..fd7391c13bc 100644 --- a/crates/external_services/Cargo.toml +++ b/crates/external_services/Cargo.toml @@ -13,7 +13,7 @@ email = ["dep:aws-config"] aws_s3 = ["dep:aws-config", "dep:aws-sdk-s3"] hashicorp-vault = ["dep:vaultrs"] v1 = ["hyperswitch_interfaces/v1"] -dynamic_routing = ["dep:prost", "dep:tonic", "dep:tonic-reflection", "dep:tonic-types", "dep:api_models", "tokio/macros", "tokio/rt-multi-thread"] +dynamic_routing = ["dep:prost", "dep:tonic", "dep:tonic-reflection", "dep:tonic-types", "dep:api_models", "tokio/macros", "tokio/rt-multi-thread" , "dep:tonic-build", "dep:router_env"] [dependencies] async-trait = "0.1.79" @@ -49,7 +49,8 @@ api_models = { version = "0.1.0", path = "../api_models", optional = true } [build-dependencies] -tonic-build = "0.12" +tonic-build = { version = "0.12" , optional = true } +router_env = { version = "0.1.0", path = "../router_env", default-features = false, optional = true } [lints] workspace = true diff --git a/crates/external_services/build.rs b/crates/external_services/build.rs index 61e19f308d8..605ef699715 100644 --- a/crates/external_services/build.rs +++ b/crates/external_services/build.rs @@ -1,15 +1,13 @@ -use std::{env, path::PathBuf}; - #[allow(clippy::expect_used)] fn main() -> Result<(), Box> { - // Get the directory of the current crate - let crate_dir = env::var("CARGO_MANIFEST_DIR")?; - let proto_file = PathBuf::from(crate_dir) - .join("..") - .join("..") - .join("proto") - .join("success_rate.proto"); - // Compile the .proto file - tonic_build::compile_protos(proto_file).expect("Failed to compile protos "); + #[cfg(feature = "dynamic_routing")] + { + // Get the directory of the current crate + let proto_file = router_env::workspace_path() + .join("proto") + .join("success_rate.proto"); + // Compile the .proto file + tonic_build::compile_protos(proto_file).expect("Failed to compile success rate proto file"); + } Ok(()) } diff --git a/crates/external_services/src/grpc_client/dynamic_routing.rs b/crates/external_services/src/grpc_client/dynamic_routing.rs index 17bd43ca3a8..2681bffb0ce 100644 --- a/crates/external_services/src/grpc_client/dynamic_routing.rs +++ b/crates/external_services/src/grpc_client/dynamic_routing.rs @@ -112,12 +112,12 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { let params = success_rate_based_config .params .map(|vec| { - vec.into_iter().fold(String::new(), |mut acc_vec, params| { - if !acc_vec.is_empty() { - acc_vec.push(':') + vec.into_iter().fold(String::new(), |mut acc_str, params| { + if !acc_str.is_empty() { + acc_str.push(':') } - acc_vec.push_str(params.to_string().as_str()); - acc_vec + acc_str.push_str(params.to_string().as_str()); + acc_str }) }) .get_required_value("params") @@ -177,12 +177,12 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { let params = success_rate_based_config .params .map(|vec| { - vec.into_iter().fold(String::new(), |mut acc_vec, params| { - if !acc_vec.is_empty() { - acc_vec.push(':') + vec.into_iter().fold(String::new(), |mut acc_str, params| { + if !acc_str.is_empty() { + acc_str.push(':') } - acc_vec.push_str(params.to_string().as_str()); - acc_vec + acc_str.push_str(params.to_string().as_str()); + acc_str }) }) .get_required_value("params") From d9270ace8ddde16eca8c45ceb79af3e4d815d7cd Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Fri, 20 Sep 2024 21:21:17 +0530 Subject: [PATCH 42/48] refactor(open_banking): Refactored to consume and use headers from SDK (#5884) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/payments.rs | 2 + crates/api_models/src/pm_auth.rs | 5 + crates/common_enums/src/enums.rs | 1 + .../src/router_data.rs | 4 + .../src/router_request_types.rs | 4 + .../src/connector/plaid/transformers.rs | 26 ++++ crates/pm_auth/src/types.rs | 4 + .../src/connector/plaid/transformers.rs | 117 ++++++++++++------ crates/router/src/core/authentication.rs | 8 +- .../src/core/authentication/transformers.rs | 2 + crates/router/src/core/fraud_check.rs | 1 + .../core/fraud_check/flows/checkout_flow.rs | 4 + .../fraud_check/flows/fulfillment_flow.rs | 2 + .../core/fraud_check/flows/record_return.rs | 4 + .../src/core/fraud_check/flows/sale_flow.rs | 4 + .../fraud_check/flows/transaction_flow.rs | 4 + crates/router/src/core/mandate/utils.rs | 2 + crates/router/src/core/payments.rs | 7 ++ crates/router/src/core/payments/flows.rs | 2 + .../src/core/payments/flows/approve_flow.rs | 2 + .../src/core/payments/flows/authorize_flow.rs | 2 + .../src/core/payments/flows/cancel_flow.rs | 2 + .../src/core/payments/flows/capture_flow.rs | 2 + .../payments/flows/complete_authorize_flow.rs | 2 + .../flows/incremental_authorization_flow.rs | 2 + .../src/core/payments/flows/psync_flow.rs | 2 + .../src/core/payments/flows/reject_flow.rs | 2 + .../src/core/payments/flows/session_flow.rs | 2 + .../payments/flows/session_update_flow.rs | 1 + .../core/payments/flows/setup_mandate_flow.rs | 2 + crates/router/src/core/payments/helpers.rs | 2 + .../router/src/core/payments/transformers.rs | 19 +-- crates/router/src/core/payouts.rs | 8 +- crates/router/src/core/pm_auth.rs | 3 + crates/router/src/core/refunds.rs | 8 +- crates/router/src/core/utils.rs | 16 +++ crates/router/src/core/webhooks/utils.rs | 2 + crates/router/src/lib.rs | 2 + .../router/src/services/conversion_impls.rs | 2 + crates/router/src/types.rs | 4 + .../router/src/types/api/verify_connector.rs | 2 + crates/router/src/types/transformers.rs | 12 +- crates/router/tests/connectors/aci.rs | 4 + crates/router/tests/connectors/utils.rs | 2 + 44 files changed, 251 insertions(+), 59 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 0236ee0c51a..23c3b219b65 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -641,6 +641,8 @@ pub struct HeaderPayload { pub x_client_platform: Option, pub x_merchant_domain: Option, pub locale: Option, + pub x_app_id: Option, + pub x_redirect_uri: Option, } impl HeaderPayload { diff --git a/crates/api_models/src/pm_auth.rs b/crates/api_models/src/pm_auth.rs index 8ed52907da8..443a4c30394 100644 --- a/crates/api_models/src/pm_auth.rs +++ b/crates/api_models/src/pm_auth.rs @@ -4,6 +4,8 @@ use common_utils::{ id_type, impl_api_event_type, }; +use crate::enums as api_enums; + #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub struct LinkTokenCreateRequest { @@ -12,6 +14,9 @@ pub struct LinkTokenCreateRequest { pub payment_id: id_type::PaymentId, // payment_id to be passed in req body for redis pm_auth connector name fetch pub payment_method: PaymentMethod, // payment_method to be used for filtering pm_auth connector pub payment_method_type: PaymentMethodType, // payment_method_type to be used for filtering pm_auth connector + pub client_platform: api_enums::ClientPlatform, // Client Platform to perform platform based processing + pub android_package_name: Option, // Android Package name to be sent for Android platform + pub redirect_uri: Option, // Merchant redirect_uri to be sent in case of IOS platform } #[derive(Debug, Clone, serde::Serialize)] diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 65ef8d87255..a6a001ad06d 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -2480,6 +2480,7 @@ pub enum ClientPlatform { #[default] Web, Ios, + Android, #[serde(other)] Unknown, } diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index a9451f6156a..cd8ac85594e 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -80,6 +80,10 @@ pub struct RouterData { pub minor_amount_captured: Option, pub integrity_check: Result<(), IntegrityCheckError>, + + pub additional_merchant_data: Option, + + pub header_payload: Option, } // Different patterns of authentication. diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index ec182056f6c..93ace230f19 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -346,6 +346,8 @@ pub struct PaymentsPostProcessingData { pub customer_id: Option, pub connector_transaction_id: Option, pub country: Option, + pub connector_meta_data: Option, + pub header_payload: Option, } impl TryFrom> @@ -371,6 +373,8 @@ impl TryFrom, user: User, + android_package_name: Option, + redirect_uri: Option, } #[derive(Debug, Serialize, Eq, PartialEq)] @@ -42,6 +44,30 @@ impl TryFrom<&types::LinkTokenRouterData> for PlaidLinkTokenRequest { }, )?, }, + android_package_name: match item.request.client_platform { + api_models::enums::ClientPlatform::Android => { + Some(item.request.android_package_name.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "android_package_name", + }, + )?) + } + api_models::enums::ClientPlatform::Ios + | api_models::enums::ClientPlatform::Web + | api_models::enums::ClientPlatform::Unknown => None, + }, + redirect_uri: match item.request.client_platform { + api_models::enums::ClientPlatform::Ios => { + Some(item.request.redirect_uri.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "redirect_uri", + }, + )?) + } + api_models::enums::ClientPlatform::Android + | api_models::enums::ClientPlatform::Web + | api_models::enums::ClientPlatform::Unknown => None, + }, }) } } diff --git a/crates/pm_auth/src/types.rs b/crates/pm_auth/src/types.rs index 4c20e879d8b..878043afe50 100644 --- a/crates/pm_auth/src/types.rs +++ b/crates/pm_auth/src/types.rs @@ -3,6 +3,7 @@ pub mod api; use std::marker::PhantomData; use api::auth_service::{BankAccountCredentials, ExchangeToken, LinkToken, RecipientCreate}; +use api_models::enums as api_enums; use common_enums::{CountryAlpha2, PaymentMethod, PaymentMethodType}; use common_utils::{id_type, types}; use masking::Secret; @@ -24,6 +25,9 @@ pub struct LinkTokenRequest { pub country_codes: Option>, pub language: Option, pub user_info: Option, + pub client_platform: api_enums::ClientPlatform, + pub android_package_name: Option, + pub redirect_uri: Option, } #[derive(Debug, Clone)] diff --git a/crates/router/src/connector/plaid/transformers.rs b/crates/router/src/connector/plaid/transformers.rs index cf9c91afd0c..c4eb5a7a035 100644 --- a/crates/router/src/connector/plaid/transformers.rs +++ b/crates/router/src/connector/plaid/transformers.rs @@ -1,6 +1,5 @@ use common_enums::Currency; use common_utils::types::FloatMajorUnit; -use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -73,6 +72,8 @@ pub struct PlaidLinkTokenRequest { products: Vec, user: User, payment_initiation: PlaidPaymentInitiation, + redirect_uri: Option, + android_package_name: Option, } #[derive(Default, Debug, Serialize, Deserialize)] @@ -105,21 +106,21 @@ impl TryFrom<&PlaidRouterData<&types::PaymentsAuthorizeRouterData>> for PlaidPay .ok_or(errors::ConnectorError::MissingRequiredField { field_name: "payment_id", })?; - let recipient_val = item + let recipient_type = item .router_data - .connector_meta_data + .additional_merchant_data .as_ref() + .map(|merchant_data| match merchant_data { + api_models::admin::AdditionalMerchantData::OpenBankingRecipientData( + data, + ) => data.clone(), + }) .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "connector_customer", - })? - .peek() - .clone(); - - let recipient_type = - serde_json::from_value::(recipient_val) - .change_context(errors::ConnectorError::ParsingFailed)?; + field_name: "additional_merchant_data", + })?; + let recipient_id = match recipient_type { - types::MerchantRecipientData::ConnectorRecipientId(id) => { + api_models::admin::MerchantRecipientData::ConnectorRecipientId(id) => { Ok(id.peek().to_string()) } _ => Err(errors::ConnectorError::MissingRequiredField { @@ -159,35 +160,73 @@ impl TryFrom<&types::PaymentsSyncRouterData> for PlaidSyncRequest { impl TryFrom<&types::PaymentsPostProcessingRouterData> for PlaidLinkTokenRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsPostProcessingRouterData) -> Result { - match item.request.payment_method_data.clone() { + match item.request.payment_method_data { domain::PaymentMethodData::OpenBanking(ref data) => match data { - domain::OpenBankingData::OpenBankingPIS { .. } => Ok(Self { - client_name: "Hyperswitch".to_string(), - country_codes: item - .request - .country - .map(|code| vec![code.to_string()]) - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "billing.address.country", - })?, - language: "en".to_string(), - products: vec!["payment_initiation".to_string()], - user: User { - client_user_id: item - .request - .customer_id - .clone() - .map(|id| id.get_string_repr().to_string()) - .unwrap_or("default cust".to_string()), - }, - payment_initiation: PlaidPaymentInitiation { - payment_id: item + domain::OpenBankingData::OpenBankingPIS { .. } => { + let headers = item.header_payload.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "header_payload", + }, + )?; + + let platform = headers.x_client_platform.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "x_client_platform", + }, + )?; + + let (is_android, is_ios) = match platform { + common_enums::ClientPlatform::Android => (true, false), + common_enums::ClientPlatform::Ios => (false, true), + _ => (false, false), + }; + + Ok(Self { + client_name: "Hyperswitch".to_string(), + country_codes: item .request - .connector_transaction_id - .clone() - .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?, - }, - }), + .country + .map(|code| vec![code.to_string()]) + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "billing.address.country", + })?, + language: "en".to_string(), + products: vec!["payment_initiation".to_string()], + user: User { + client_user_id: item + .request + .customer_id + .clone() + .map(|id| id.get_string_repr().to_string()) + .unwrap_or("default cust".to_string()), + }, + payment_initiation: PlaidPaymentInitiation { + payment_id: item + .request + .connector_transaction_id + .clone() + .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?, + }, + android_package_name: if is_android { + Some(headers.x_app_id.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "x-app-id", + }, + )?) + } else { + None + }, + redirect_uri: if is_ios { + Some(headers.x_redirect_uri.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "x_redirect_uri", + }, + )?) + } else { + None + }, + }) + } }, _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), } diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index 6f1746b5277..29508b5c0f5 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -61,8 +61,12 @@ pub async fn perform_authentication( webhook_url, three_ds_requestor_url, )?; - let response = - utils::do_auth_connector_call(state, authentication_connector.clone(), router_data).await?; + let response = Box::pin(utils::do_auth_connector_call( + state, + authentication_connector.clone(), + router_data, + )) + .await?; let authentication = utils::update_trackers(state, response.clone(), authentication_data, None).await?; response diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 3372d9b355e..6013cdbe97f 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -182,6 +182,8 @@ pub fn construct_router_data( payment_method_status: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }) } diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index c696c5072bf..2217de20907 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -119,6 +119,7 @@ where customer, &merchant_connector_account, None, + None, ) .await?; diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index 74606eb5917..92dc2a99e8a 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -39,6 +39,7 @@ impl ConstructFlowSpecificData, _merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult> { todo!() @@ -54,6 +55,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -151,6 +153,8 @@ impl ConstructFlowSpecificData( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index 6603f09cad3..e4d002931ec 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -36,6 +36,7 @@ impl ConstructFlowSpecificData, _merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult> { todo!() @@ -51,6 +52,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -120,6 +122,8 @@ impl ConstructFlowSpecificData, _merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult> { todo!() } @@ -48,6 +49,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -128,6 +130,8 @@ impl ConstructFlowSpecificData, _merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult< RouterData, > { @@ -55,6 +56,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult< RouterData, > { @@ -134,6 +136,8 @@ impl dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload, }; Ok(router_data) diff --git a/crates/router/src/core/mandate/utils.rs b/crates/router/src/core/mandate/utils.rs index de4b0b77feb..544f9ea756e 100644 --- a/crates/router/src/core/mandate/utils.rs +++ b/crates/router/src/core/mandate/utils.rs @@ -78,6 +78,8 @@ pub async fn construct_mandate_revoke_router_data( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 744b93032bf..283e02f975c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -372,6 +372,7 @@ where &connector, &mut payment_data, op_ref, + Some(header_payload.clone()), ) .await?; } @@ -496,6 +497,7 @@ where &connector_data, &mut payment_data, op_ref, + Some(header_payload.clone()), ) .await?; } @@ -1642,6 +1644,7 @@ where customer, &merchant_connector_account, merchant_recipient_data, + None, ) .await?; @@ -1975,6 +1978,7 @@ where customer, &merchant_connector_account, None, + None, ) .await?; @@ -2120,6 +2124,7 @@ where customer, merchant_connector_account, None, + None, ) .await?; @@ -2304,6 +2309,7 @@ async fn complete_postprocessing_steps_if_required( connector: &api::ConnectorData, payment_data: &mut D, _operation: &BoxedOperation<'_, F, Q, D>, + header_payload: Option, ) -> RouterResult> where F: Send + Clone + Sync, @@ -2324,6 +2330,7 @@ where customer, merchant_conn_account, None, + header_payload, ) .await?; diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 245e8ae629b..e364d662e24 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -38,6 +38,7 @@ pub trait ConstructFlowSpecificData { customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult>; #[cfg(all(feature = "v2", feature = "customer_v2"))] @@ -50,6 +51,7 @@ pub trait ConstructFlowSpecificData { _customer: &Option, _merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult>; async fn get_merchant_recipient_data<'a>( diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index 4ede7c50854..c91de9eb3a7 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -25,6 +25,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Approve, @@ -38,6 +39,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index f4bf869daa6..6fbe98b3a47 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -37,6 +37,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult< types::RouterData< api::Authorize, @@ -56,6 +57,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index fb6ed6f3ac7..1f212cc631b 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -25,6 +25,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Void, @@ -38,6 +39,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Capture, @@ -38,6 +39,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index 35038695f0c..0e27e4f37ad 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -29,6 +29,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult< types::RouterData< api::CompleteAuthorize, @@ -48,6 +49,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 6452d279135..7a3f9f0ac44 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -28,6 +28,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::IncrementalAuthorization, @@ -41,6 +42,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 9c06cc7fd1a..2bb8fc19083 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -26,6 +26,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult< types::RouterData, > { @@ -41,6 +42,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Reject, @@ -37,6 +38,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Session, @@ -53,6 +54,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/flows/session_update_flow.rs b/crates/router/src/core/payments/flows/session_update_flow.rs index 3ada522a61f..6cad2500814 100644 --- a/crates/router/src/core/payments/flows/session_update_flow.rs +++ b/crates/router/src/core/payments/flows/session_update_flow.rs @@ -28,6 +28,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult { Box::pin( transformers::construct_router_data_to_update_calculated_tax::< diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index f9a5458840e..69720b870a2 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -32,6 +32,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::SetupMandate, @@ -45,6 +46,7 @@ impl customer, merchant_connector_account, merchant_recipient_data, + header_payload, )) .await } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index b0e2f61d184..ded12b781d4 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3744,6 +3744,8 @@ pub fn router_data_type_conversion( connector_response: router_data.connector_response, integrity_check: Ok(()), connector_wallets_details: router_data.connector_wallets_details, + additional_merchant_data: router_data.additional_merchant_data, + header_payload: router_data.header_payload, } } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 22c047edfcb..37e178dc7f3 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -151,6 +151,8 @@ where dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } @@ -167,6 +169,7 @@ pub async fn construct_payment_router_data<'a, F, T>( _customer: &'a Option, _merchant_connector_account: &helpers::MerchantConnectorAccountType, _merchant_recipient_data: Option, + _header_payload: Option, ) -> RouterResult> where T: TryFrom>, @@ -190,6 +193,7 @@ pub async fn construct_payment_router_data<'a, F, T>( customer: &'a Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, + header_payload: Option, ) -> RouterResult> where T: TryFrom>, @@ -318,14 +322,7 @@ where .payment_attempt .authentication_type .unwrap_or_default(), - connector_meta_data: if let Some(data) = merchant_recipient_data { - let val = serde_json::to_value(data) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while encoding MerchantRecipientData")?; - Some(Secret::new(val)) - } else { - merchant_connector_account.get_metadata() - }, + connector_meta_data: merchant_connector_account.get_metadata(), connector_wallets_details: merchant_connector_account.get_connector_wallets_details(), request: T::try_from(additional_data)?, response, @@ -364,6 +361,12 @@ where dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: merchant_recipient_data.map(|data| { + api_models::admin::AdditionalMerchantData::foreign_from( + types::AdditionalMerchantData::OpenBankingRecipientData(data), + ) + }), + header_payload, }; Ok(router_data) diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index e40c0f353aa..c96da24443c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -730,13 +730,13 @@ pub async fn payouts_fulfill_core( .await? .get_required_value("payout_method_data")?, ); - fulfill_payout( + Box::pin(fulfill_payout( &state, &merchant_account, &key_store, &connector_data, &mut payout_data, - ) + )) .await .attach_printable("Payout fulfillment failed for given Payout request")?; @@ -1138,13 +1138,13 @@ pub async fn call_connector_payout( // Auto fulfillment flow let status = payout_data.payout_attempt.status; if payouts.auto_fulfill && status == storage_enums::PayoutStatus::RequiresFulfillment { - fulfill_payout( + Box::pin(fulfill_payout( state, merchant_account, key_store, connector_data, payout_data, - ) + )) .await .attach_printable("Payout fulfillment failed for given Payout request")?; } diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index d6d4df67000..a09ca4fc6e6 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -165,6 +165,9 @@ pub async fn create_link_token( )?]), language: payload.language, user_info: payment_intent.and_then(|pi| pi.customer_id), + client_platform: payload.client_platform, + android_package_name: payload.android_package_name, + redirect_uri: payload.redirect_uri, }, response: Ok(pm_auth_types::LinkTokenResponse { link_token: "".to_string(), diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index 118f4b81766..0436ba2906e 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -1185,7 +1185,7 @@ pub async fn schedule_refund_execution( Ok(refund) } api_models::refunds::RefundType::Instant => { - let update_refund = trigger_refund_to_gateway( + let update_refund = Box::pin(trigger_refund_to_gateway( state, &refund, merchant_account, @@ -1194,7 +1194,7 @@ pub async fn schedule_refund_execution( payment_intent, creds_identifier, charges, - ) + )) .await; match update_refund { @@ -1438,7 +1438,7 @@ pub async fn trigger_refund_execute_workflow( }; //trigger refund request to gateway - let updated_refund = trigger_refund_to_gateway( + let updated_refund = Box::pin(trigger_refund_to_gateway( state, &refund, &merchant_account, @@ -1447,7 +1447,7 @@ pub async fn trigger_refund_execute_workflow( &payment_intent, None, charges, - ) + )) .await?; add_refund_sync_task( db, diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 53d97b43d9c..607de45215a 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -215,6 +215,8 @@ pub async fn construct_payout_router_data<'a, F>( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) @@ -389,6 +391,8 @@ pub async fn construct_refund_router_data<'a, F>( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) @@ -697,6 +701,8 @@ pub async fn construct_accept_dispute_router_data<'a>( refund_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } @@ -790,6 +796,8 @@ pub async fn construct_submit_evidence_router_data<'a>( dispute_id: Some(dispute.dispute_id.clone()), connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } @@ -889,6 +897,8 @@ pub async fn construct_upload_file_router_data<'a>( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } @@ -1008,6 +1018,8 @@ pub async fn construct_payments_dynamic_tax_calculation_router_data<'a, F: Clone payment_method_status: None, minor_amount_captured: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } @@ -1104,6 +1116,8 @@ pub async fn construct_defend_dispute_router_data<'a>( dispute_id: Some(dispute.dispute_id.clone()), connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } @@ -1194,6 +1208,8 @@ pub async fn construct_retrieve_file_router_data<'a>( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index 8987a9384df..8680e43eff3 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -120,6 +120,8 @@ pub async fn construct_webhook_router_data<'a>( dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, }; Ok(router_data) } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index b6ac439a5c4..c1eac13832b 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -83,6 +83,8 @@ pub mod headers { pub const BROWSER_NAME: &str = "x-browser-name"; pub const X_CLIENT_PLATFORM: &str = "x-client-platform"; pub const X_MERCHANT_DOMAIN: &str = "x-merchant-domain"; + pub const X_APP_ID: &str = "x-app-id"; + pub const X_REDIRECT_URI: &str = "x-redirect-uri"; pub const X_TENANT_ID: &str = "x-tenant-id"; } diff --git a/crates/router/src/services/conversion_impls.rs b/crates/router/src/services/conversion_impls.rs index f1aec19bd30..22930916093 100644 --- a/crates/router/src/services/conversion_impls.rs +++ b/crates/router/src/services/conversion_impls.rs @@ -74,6 +74,8 @@ fn get_default_router_data( payment_method_status: None, minor_amount_captured: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 3a56271b08a..30bb6630684 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -907,6 +907,8 @@ impl ForeignFrom<(&RouterData, T2) refund_id: data.refund_id.clone(), connector_response: data.connector_response.clone(), integrity_check: Ok(()), + additional_merchant_data: data.additional_merchant_data.clone(), + header_payload: data.header_payload.clone(), } } } @@ -969,6 +971,8 @@ impl dispute_id: None, connector_response: data.connector_response.clone(), integrity_check: Ok(()), + additional_merchant_data: data.additional_merchant_data.clone(), + header_payload: data.header_payload.clone(), } } } diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 1b3162a3549..6c92b1b801a 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -113,6 +113,8 @@ impl VerifyConnectorData { dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, } } } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index bfad15e5d9d..992816a3616 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -20,8 +20,8 @@ use super::domain; use crate::{ core::errors, headers::{ - ACCEPT_LANGUAGE, BROWSER_NAME, X_CLIENT_PLATFORM, X_CLIENT_SOURCE, X_CLIENT_VERSION, - X_MERCHANT_DOMAIN, X_PAYMENT_CONFIRM_SOURCE, + ACCEPT_LANGUAGE, BROWSER_NAME, X_APP_ID, X_CLIENT_PLATFORM, X_CLIENT_SOURCE, + X_CLIENT_VERSION, X_MERCHANT_DOMAIN, X_PAYMENT_CONFIRM_SOURCE, X_REDIRECT_URI, }, services::authentication::get_header_value_by_key, types::{ @@ -1455,6 +1455,12 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { let x_merchant_domain = get_header_value_by_key(X_MERCHANT_DOMAIN.into(), headers)?.map(|val| val.to_string()); + let x_app_id = + get_header_value_by_key(X_APP_ID.into(), headers)?.map(|val| val.to_string()); + + let x_redirect_uri = + get_header_value_by_key(X_REDIRECT_URI.into(), headers)?.map(|val| val.to_string()); + Ok(Self { payment_confirm_source, client_source, @@ -1464,6 +1470,8 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { x_client_platform, x_merchant_domain, locale, + x_app_id, + x_redirect_uri, }) } } diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index f3ff1a60361..5d48a0188ff 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -126,6 +126,8 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { refund_id: None, dispute_id: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, } } @@ -193,6 +195,8 @@ fn construct_refund_router_data() -> types::RefundsRouterData { refund_id: None, dispute_id: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, } } diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index ea12d463fbd..582c212c241 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -544,6 +544,8 @@ pub trait ConnectorActions: Connector { dispute_id: None, connector_response: None, integrity_check: Ok(()), + additional_merchant_data: None, + header_payload: None, } } From 37925626e6446900f1d16e0e5f184ee472d4be3e Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Sun, 22 Sep 2024 19:11:52 +0530 Subject: [PATCH 43/48] refactor(router): add `email` in `billing` and `shipping` address of merchant payment method list (#5981) --- crates/router/src/configs/defaults.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index 41efc6e4c38..5719e111759 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -12353,6 +12353,15 @@ pub fn get_billing_required_fields() -> HashMap { value: None, }, ), + ( + "billing.email".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + }, + ), ]) } @@ -12441,6 +12450,15 @@ pub fn get_shipping_required_fields() -> HashMap { value: None, }, ), + ( + "shipping.email".to_string(), + RequiredFieldInfo { + required_field: "shipping.email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + }, + ), ]) } From 365f5680b77023865b21364a14c21349075aef19 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit <64925866+apoorvdixit88@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:39:37 +0530 Subject: [PATCH 44/48] feat(disputes): add filters for disputes list (#5637) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/disputes.rs | 90 ++++++++---- crates/api_models/src/events.rs | 8 +- crates/common_enums/src/enums.rs | 1 + .../hyperswitch_domain_models/src/disputes.rs | 89 ++++++++++++ crates/hyperswitch_domain_models/src/lib.rs | 1 + crates/router/src/core/disputes.rs | 70 ++++++++- crates/router/src/db/dispute.rs | 135 ++++++++++-------- crates/router/src/db/kafka_store.rs | 7 +- crates/router/src/routes/app.rs | 5 + crates/router/src/routes/disputes.rs | 81 ++++++++++- crates/router/src/routes/lock_utils.rs | 1 + crates/router/src/routes/refunds.rs | 2 +- crates/router/src/types/storage/dispute.rs | 79 +++++++--- crates/router_env/src/logger/types.rs | 2 + 14 files changed, 446 insertions(+), 125 deletions(-) create mode 100644 crates/hyperswitch_domain_models/src/disputes.rs diff --git a/crates/api_models/src/disputes.rs b/crates/api_models/src/disputes.rs index a1340b5aa5a..4ddb50af0f2 100644 --- a/crates/api_models/src/disputes.rs +++ b/crates/api_models/src/disputes.rs @@ -1,11 +1,13 @@ use std::collections::HashMap; +use common_utils::types::TimeRange; use masking::{Deserialize, Serialize}; +use serde::de::Error; use time::PrimitiveDateTime; use utoipa::ToSchema; use super::enums::{DisputeStage, DisputeStatus}; -use crate::files; +use crate::{admin::MerchantConnectorInfo, enums, files}; #[derive(Clone, Debug, Serialize, ToSchema, Eq, PartialEq)] pub struct DisputeResponse { @@ -108,41 +110,51 @@ pub struct DisputeEvidenceBlock { pub file_metadata_response: files::FileMetadataResponse, } -#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)] #[serde(deny_unknown_fields)] -pub struct DisputeListConstraints { - /// limit on the number of objects to return - pub limit: Option, +pub struct DisputeListGetConstraints { + /// The identifier for dispute + pub dispute_id: Option, + /// The payment_id against which dispute is raised + pub payment_id: Option, + /// Limit on the number of objects to return + pub limit: Option, + /// The starting point within a list of object + pub offset: Option, /// The identifier for business profile #[schema(value_type = Option)] pub profile_id: Option, - /// status of the dispute - pub dispute_status: Option, - /// stage of the dispute - pub dispute_stage: Option, - /// reason for the dispute + /// The comma separated list of status of the disputes + #[serde(default, deserialize_with = "parse_comma_separated")] + pub dispute_status: Option>, + /// The comma separated list of stages of the disputes + #[serde(default, deserialize_with = "parse_comma_separated")] + pub dispute_stage: Option>, + /// Reason for the dispute pub reason: Option, - /// connector linked to dispute - pub connector: Option, - /// The time at which dispute is received - #[schema(example = "2022-09-10T10:11:12Z")] - pub received_time: Option, - /// Time less than the dispute received time - #[schema(example = "2022-09-10T10:11:12Z")] - #[serde(rename = "received_time.lt")] - pub received_time_lt: Option, - /// Time greater than the dispute received time - #[schema(example = "2022-09-10T10:11:12Z")] - #[serde(rename = "received_time.gt")] - pub received_time_gt: Option, - /// Time less than or equals to the dispute received time - #[schema(example = "2022-09-10T10:11:12Z")] - #[serde(rename = "received_time.lte")] - pub received_time_lte: Option, - /// Time greater than or equals to the dispute received time - #[schema(example = "2022-09-10T10:11:12Z")] - #[serde(rename = "received_time.gte")] - pub received_time_gte: Option, + /// The comma separated list of connectors linked to disputes + #[serde(default, deserialize_with = "parse_comma_separated")] + pub connector: Option>, + /// The comma separated list of currencies of the disputes + #[serde(default, deserialize_with = "parse_comma_separated")] + pub currency: Option>, + /// The merchant connector id to filter the disputes list + pub merchant_connector_id: Option, + /// The time range for which objects are needed. TimeRange has two fields start_time and end_time from which objects can be filtered as per required scenarios (created_at, time less than, greater than etc). + #[serde(flatten)] + pub time_range: Option, +} + +#[derive(Clone, Debug, serde::Serialize, ToSchema)] +pub struct DisputeListFilters { + /// The map of available connector filters, where the key is the connector name and the value is a list of MerchantConnectorInfo instances + pub connector: HashMap>, + /// The list of available currency filters + pub currency: Vec, + /// The list of available dispute status filters + pub dispute_status: Vec, + /// The list of available dispute stage filters + pub dispute_stage: Vec, } #[derive(Default, Clone, Debug, Serialize, Deserialize, ToSchema)] @@ -216,3 +228,19 @@ pub struct DisputesAggregateResponse { /// Different status of disputes with their count pub status_with_count: HashMap, } + +fn parse_comma_separated<'de, D, T>(v: D) -> Result>, D::Error> +where + D: serde::Deserializer<'de>, + T: std::str::FromStr, + ::Err: std::fmt::Debug + std::fmt::Display + std::error::Error, +{ + let output = Option::<&str>::deserialize(v)?; + output + .map(|s| { + s.split(",") + .map(|x| x.parse::().map_err(D::Error::custom)) + .collect::>() + }) + .transpose() +} diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 29fe0bed220..b8a872ffd25 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -75,7 +75,7 @@ impl_api_event_type!( RetrievePaymentLinkRequest, PaymentLinkListConstraints, MandateId, - DisputeListConstraints, + DisputeListGetConstraints, RetrieveApiKeyResponse, ProfileResponse, ProfileUpdate, @@ -171,3 +171,9 @@ impl ApiEventMetric for PaymentMethodIntentCreate { Some(ApiEventsType::PaymentMethodCreate) } } + +impl ApiEventMetric for DisputeListFilters { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index a6a001ad06d..42e05b27cec 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1817,6 +1817,7 @@ pub enum CardNetwork { serde::Deserialize, serde::Serialize, strum::Display, + strum::EnumIter, strum::EnumString, ToSchema, )] diff --git a/crates/hyperswitch_domain_models/src/disputes.rs b/crates/hyperswitch_domain_models/src/disputes.rs new file mode 100644 index 00000000000..87ae6204ac7 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/disputes.rs @@ -0,0 +1,89 @@ +use crate::errors; + +pub struct DisputeListConstraints { + pub dispute_id: Option, + pub payment_id: Option, + pub limit: Option, + pub offset: Option, + pub profile_id: Option>, + pub dispute_status: Option>, + pub dispute_stage: Option>, + pub reason: Option, + pub connector: Option>, + pub merchant_connector_id: Option, + pub currency: Option>, + pub time_range: Option, +} + +impl + TryFrom<( + api_models::disputes::DisputeListGetConstraints, + Option>, + )> for DisputeListConstraints +{ + type Error = error_stack::Report; + fn try_from( + (value, auth_profile_id_list): ( + api_models::disputes::DisputeListGetConstraints, + Option>, + ), + ) -> Result { + let api_models::disputes::DisputeListGetConstraints { + dispute_id, + payment_id, + limit, + offset, + profile_id, + dispute_status, + dispute_stage, + reason, + connector, + merchant_connector_id, + currency, + time_range, + } = value; + let profile_id_from_request_body = profile_id; + // Match both the profile ID from the request body and the list of authenticated profile IDs coming from auth layer + let profile_id_list = match (profile_id_from_request_body, auth_profile_id_list) { + (None, None) => None, + // Case when the request body profile ID is None, but authenticated profile IDs are available, return the auth list + (None, Some(auth_profile_id_list)) => Some(auth_profile_id_list), + // Case when the request body profile ID is provided, but the auth list is None, create a vector with the request body profile ID + (Some(profile_id_from_request_body), None) => Some(vec![profile_id_from_request_body]), + (Some(profile_id_from_request_body), Some(auth_profile_id_list)) => { + // Check if the profile ID from the request body is present in the authenticated profile ID list + let profile_id_from_request_body_is_available_in_auth_profile_id_list = + auth_profile_id_list.contains(&profile_id_from_request_body); + + if profile_id_from_request_body_is_available_in_auth_profile_id_list { + Some(vec![profile_id_from_request_body]) + } else { + // If the profile ID is not valid, return an error indicating access is not available + return Err(error_stack::Report::new( + errors::api_error_response::ApiErrorResponse::PreconditionFailed { + message: format!( + "Access not available for the given profile_id {:?}", + profile_id_from_request_body + ), + }, + )); + } + } + }; + + Ok(Self { + dispute_id, + payment_id, + limit, + offset, + profile_id: profile_id_list, + dispute_status, + dispute_stage, + reason, + connector, + merchant_connector_id, + currency, + time_range, + }) + } +} diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index b0a660ad5d3..c99fbd89cd5 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -3,6 +3,7 @@ pub mod behaviour; pub mod business_profile; pub mod consts; pub mod customer; +pub mod disputes; pub mod errors; pub mod mandates; pub mod merchant_account; diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 30b73eb4c6a..8f083c3103a 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -1,11 +1,13 @@ -use api_models::{disputes as dispute_models, files as files_api_models}; +use std::collections::HashMap; + +use api_models::{ + admin::MerchantConnectorInfo, disputes as dispute_models, files as files_api_models, +}; use common_utils::ext_traits::{Encode, ValueExt}; use error_stack::ResultExt; use router_env::{instrument, tracing}; -pub mod transformers; -use std::collections::HashMap; - use strum::IntoEnumIterator; +pub mod transformers; use super::{ errors::{self, ConnectorErrorExt, RouterResponse, StorageErrorExt}, @@ -48,12 +50,13 @@ pub async fn retrieve_dispute( pub async fn retrieve_disputes_list( state: SessionState, merchant_account: domain::MerchantAccount, - _profile_id_list: Option>, - constraints: api_models::disputes::DisputeListConstraints, + profile_id_list: Option>, + constraints: api_models::disputes::DisputeListGetConstraints, ) -> RouterResponse> { + let dispute_list_constraints = &(constraints.clone(), profile_id_list.clone()).try_into()?; let disputes = state .store - .find_disputes_by_merchant_id(merchant_account.get_id(), constraints) + .find_disputes_by_constraints(merchant_account.get_id(), dispute_list_constraints) .await .to_not_found_response(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to retrieve disputes")?; @@ -76,6 +79,59 @@ pub async fn accept_dispute( todo!() } +#[instrument(skip(state))] +pub async fn get_filters_for_disputes( + state: SessionState, + merchant_account: domain::MerchantAccount, + profile_id_list: Option>, +) -> RouterResponse { + let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) = + super::admin::list_payment_connectors( + state, + merchant_account.get_id().to_owned(), + profile_id_list, + ) + .await? + { + data + } else { + return Err(error_stack::report!( + errors::ApiErrorResponse::InternalServerError + )) + .attach_printable( + "Failed to retrieve merchant connector accounts while fetching dispute list filters.", + ); + }; + + let connector_map = merchant_connector_accounts + .into_iter() + .filter_map(|merchant_connector_account| { + merchant_connector_account + .connector_label + .clone() + .map(|label| { + let info = merchant_connector_account.to_merchant_connector_info(&label); + (merchant_connector_account.connector_name, info) + }) + }) + .fold( + HashMap::new(), + |mut map: HashMap>, (connector_name, info)| { + map.entry(connector_name).or_default().push(info); + map + }, + ); + + Ok(services::ApplicationResponse::Json( + api_models::disputes::DisputeListFilters { + connector: connector_map, + currency: storage_enums::Currency::iter().collect(), + dispute_status: storage_enums::DisputeStatus::iter().collect(), + dispute_stage: storage_enums::DisputeStage::iter().collect(), + }, + )) +} + #[cfg(feature = "v1")] #[instrument(skip(state))] pub async fn accept_dispute( diff --git a/crates/router/src/db/dispute.rs b/crates/router/src/db/dispute.rs index a44528fde43..d7f4af63015 100644 --- a/crates/router/src/db/dispute.rs +++ b/crates/router/src/db/dispute.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use error_stack::report; +use hyperswitch_domain_models::disputes; use router_env::{instrument, tracing}; use super::{MockDb, Store}; @@ -30,10 +31,10 @@ pub trait DisputeInterface { dispute_id: &str, ) -> CustomResult; - async fn find_disputes_by_merchant_id( + async fn find_disputes_by_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - dispute_constraints: api_models::disputes::DisputeListConstraints, + dispute_constraints: &disputes::DisputeListConstraints, ) -> CustomResult, errors::StorageError>; async fn find_disputes_by_merchant_id_payment_id( @@ -101,10 +102,10 @@ impl DisputeInterface for Store { } #[instrument(skip_all)] - async fn find_disputes_by_merchant_id( + async fn find_disputes_by_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - dispute_constraints: api_models::disputes::DisputeListConstraints, + dispute_constraints: &disputes::DisputeListConstraints, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage::Dispute::filter_by_constraints(&conn, merchant_id, dispute_constraints) @@ -238,75 +239,96 @@ impl DisputeInterface for MockDb { .into()) } - async fn find_disputes_by_merchant_id( + async fn find_disputes_by_constraints( &self, merchant_id: &common_utils::id_type::MerchantId, - dispute_constraints: api_models::disputes::DisputeListConstraints, + dispute_constraints: &disputes::DisputeListConstraints, ) -> CustomResult, errors::StorageError> { let locked_disputes = self.disputes.lock().await; - - Ok(locked_disputes + let limit_usize = dispute_constraints + .limit + .unwrap_or(u32::MAX) + .try_into() + .unwrap_or(usize::MAX); + let offset_usize = dispute_constraints + .offset + .unwrap_or(0) + .try_into() + .unwrap_or(usize::MIN); + let filtered_disputes: Vec = locked_disputes .iter() - .filter(|d| { - d.merchant_id == *merchant_id + .filter(|dispute| { + dispute.merchant_id == *merchant_id && dispute_constraints - .dispute_status + .dispute_id .as_ref() - .map(|status| status == &d.dispute_status) - .unwrap_or(true) + .map_or(true, |id| &dispute.dispute_id == id) && dispute_constraints - .dispute_stage + .payment_id .as_ref() - .map(|stage| stage == &d.dispute_stage) - .unwrap_or(true) + .map_or(true, |id| &dispute.payment_id == id) && dispute_constraints - .reason + .profile_id .as_ref() - .and_then(|reason| { - d.connector_reason + .map_or(true, |profile_ids| { + dispute + .profile_id .as_ref() - .map(|connector_reason| connector_reason == reason) + .map_or(true, |id| profile_ids.contains(id)) }) - .unwrap_or(true) && dispute_constraints - .connector + .dispute_status .as_ref() - .map(|connector| connector == &d.connector) - .unwrap_or(true) + .map_or(true, |statuses| statuses.contains(&dispute.dispute_status)) && dispute_constraints - .received_time + .dispute_stage .as_ref() - .map(|received_time| received_time == &d.created_at) - .unwrap_or(true) + .map_or(true, |stages| stages.contains(&dispute.dispute_stage)) + && dispute_constraints.reason.as_ref().map_or(true, |reason| { + dispute + .connector_reason + .as_ref() + .map_or(true, |d_reason| d_reason == reason) + }) && dispute_constraints - .received_time_lt + .connector .as_ref() - .map(|received_time_lt| received_time_lt > &d.created_at) - .unwrap_or(true) + .map_or(true, |connectors| { + connectors + .iter() + .any(|connector| dispute.connector.as_str() == *connector) + }) && dispute_constraints - .received_time_gt + .merchant_connector_id .as_ref() - .map(|received_time_gt| received_time_gt < &d.created_at) - .unwrap_or(true) + .map_or(true, |id| { + dispute.merchant_connector_id.as_ref() == Some(id) + }) && dispute_constraints - .received_time_lte + .currency .as_ref() - .map(|received_time_lte| received_time_lte >= &d.created_at) - .unwrap_or(true) + .map_or(true, |currencies| { + currencies + .iter() + .any(|currency| dispute.currency.as_str() == currency.to_string()) + }) && dispute_constraints - .received_time_gte + .time_range .as_ref() - .map(|received_time_gte| received_time_gte <= &d.created_at) - .unwrap_or(true) + .map_or(true, |range| { + let dispute_time = dispute.created_at; + dispute_time >= range.start_time + && range + .end_time + .map_or(true, |end_time| dispute_time <= end_time) + }) }) - .take( - dispute_constraints - .limit - .and_then(|limit| usize::try_from(limit).ok()) - .unwrap_or(usize::MAX), - ) + .skip(offset_usize) + .take(limit_usize) .cloned() - .collect()) + .collect(); + + Ok(filtered_disputes) } async fn find_disputes_by_merchant_id_payment_id( @@ -437,11 +459,11 @@ mod tests { mod mockdb_dispute_interface { use std::borrow::Cow; - use api_models::disputes::DisputeListConstraints; use diesel_models::{ dispute::DisputeNew, enums::{DisputeStage, DisputeStatus}, }; + use hyperswitch_domain_models::disputes::DisputeListConstraints; use masking::Secret; use redis_interface::RedisSettings; use serde_json::Value; @@ -648,20 +670,21 @@ mod tests { .unwrap(); let found_disputes = mockdb - .find_disputes_by_merchant_id( + .find_disputes_by_constraints( &merchant_id, - DisputeListConstraints { + &DisputeListConstraints { + dispute_id: None, + payment_id: None, + profile_id: None, + connector: None, + merchant_connector_id: None, + currency: None, limit: None, + offset: None, dispute_status: None, dispute_stage: None, reason: None, - connector: None, - received_time: None, - received_time_lt: None, - received_time_gt: None, - received_time_lte: None, - received_time_gte: None, - profile_id: None, + time_range: None, }, ) .await diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 21e00a7f549..90b3e5a6837 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -14,6 +14,7 @@ use hyperswitch_domain_models::payouts::{ payout_attempt::PayoutAttemptInterface, payouts::PayoutsInterface, }; use hyperswitch_domain_models::{ + disputes, payments::{payment_attempt::PaymentAttemptInterface, payment_intent::PaymentIntentInterface}, refunds, }; @@ -590,13 +591,13 @@ impl DisputeInterface for KafkaStore { .await } - async fn find_disputes_by_merchant_id( + async fn find_disputes_by_constraints( &self, merchant_id: &id_type::MerchantId, - dispute_constraints: api_models::disputes::DisputeListConstraints, + dispute_constraints: &disputes::DisputeListConstraints, ) -> CustomResult, errors::StorageError> { self.diesel_store - .find_disputes_by_merchant_id(merchant_id, dispute_constraints) + .find_disputes_by_constraints(merchant_id, dispute_constraints) .await } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 01a48c56401..dafee54ae05 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1496,6 +1496,11 @@ impl Disputes { web::resource("/profile/list") .route(web::get().to(disputes::retrieve_disputes_list_profile)), ) + .service(web::resource("/filter").route(web::get().to(disputes::get_disputes_filters))) + .service( + web::resource("/profile/filter") + .route(web::get().to(disputes::get_disputes_filters_profile)), + ) .service( web::resource("/accept/{dispute_id}") .route(web::post().to(disputes::accept_dispute)), diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index 50a26ceae89..1a8c4410a53 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -87,10 +87,10 @@ pub async fn retrieve_dispute( pub async fn retrieve_disputes_list( state: web::Data, req: HttpRequest, - payload: web::Query, + query: web::Query, ) -> HttpResponse { let flow = Flow::DisputesList; - let payload = payload.into_inner(); + let payload = query.into_inner(); Box::pin(api::server_wrap( flow, state, @@ -140,7 +140,7 @@ pub async fn retrieve_disputes_list( pub async fn retrieve_disputes_list_profile( state: web::Data, req: HttpRequest, - payload: web::Query, + payload: web::Query, ) -> HttpResponse { let flow = Flow::DisputesList; let payload = payload.into_inner(); @@ -170,6 +170,81 @@ pub async fn retrieve_disputes_list_profile( .await } +/// Disputes - Disputes Filters +#[utoipa::path( + get, + path = "/disputes/filter", + responses( + (status = 200, description = "List of filters", body = DisputeListFilters), + ), + tag = "Disputes", + operation_id = "List all filters for disputes", + security(("api_key" = [])) +)] +#[instrument(skip_all, fields(flow = ?Flow::DisputesFilters))] +pub async fn get_disputes_filters(state: web::Data, req: HttpRequest) -> HttpResponse { + let flow = Flow::DisputesFilters; + Box::pin(api::server_wrap( + flow, + state, + &req, + (), + |state, auth, _, _| disputes::get_filters_for_disputes(state, auth.merchant_account, None), + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: Permission::DisputeRead, + minimum_entity_level: EntityType::Merchant, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +/// Disputes - Disputes Filters Profile +#[utoipa::path( + get, + path = "/disputes/profile/filter", + responses( + (status = 200, description = "List of filters", body = DisputeListFilters), + ), + tag = "Disputes", + operation_id = "List all filters for disputes", + security(("api_key" = [])) +)] +#[instrument(skip_all, fields(flow = ?Flow::DisputesFilters))] +pub async fn get_disputes_filters_profile( + state: web::Data, + req: HttpRequest, +) -> HttpResponse { + let flow = Flow::DisputesFilters; + Box::pin(api::server_wrap( + flow, + state, + &req, + (), + |state, auth, _, _| { + disputes::get_filters_for_disputes( + state, + auth.merchant_account, + auth.profile_id.map(|profile_id| vec![profile_id]), + ) + }, + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: Permission::DisputeRead, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + /// Disputes - Accept Dispute #[utoipa::path( get, diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index d6d3ec8a952..482bc40c49f 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -172,6 +172,7 @@ impl From for ApiIdentifier { Flow::DisputesRetrieve | Flow::DisputesList + | Flow::DisputesFilters | Flow::DisputesEvidenceSubmit | Flow::AttachDisputeEvidence | Flow::RetrieveDisputeEvidence diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index 40184c5fcfa..b287b5e9790 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -381,7 +381,7 @@ pub async fn get_refunds_filters(state: web::Data, req: HttpRequest) - /// To list the refunds filters associated with list of connectors, currencies and payment statuses #[utoipa::path( get, - path = "/refunds/v2/filter/profile", + path = "/refunds/v2/profile/filter", responses( (status = 200, description = "List of static filters", body = RefundListFilters), ), diff --git a/crates/router/src/types/storage/dispute.rs b/crates/router/src/types/storage/dispute.rs index 45f2073708b..28a9573b333 100644 --- a/crates/router/src/types/storage/dispute.rs +++ b/crates/router/src/types/storage/dispute.rs @@ -4,6 +4,7 @@ use diesel::{associations::HasTable, ExpressionMethods, QueryDsl}; pub use diesel_models::dispute::{Dispute, DisputeNew, DisputeUpdate}; use diesel_models::{errors, query::generics::db_metrics, schema::dispute::dsl}; use error_stack::ResultExt; +use hyperswitch_domain_models::disputes; use crate::{connection::PgPooledConn, logger}; @@ -12,7 +13,7 @@ pub trait DisputeDbExt: Sized { async fn filter_by_constraints( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, - dispute_list_constraints: api_models::disputes::DisputeListConstraints, + dispute_list_constraints: &disputes::DisputeListConstraints, ) -> CustomResult, errors::DatabaseError>; async fn get_dispute_status_with_count( @@ -28,45 +29,77 @@ impl DisputeDbExt for Dispute { async fn filter_by_constraints( conn: &PgPooledConn, merchant_id: &common_utils::id_type::MerchantId, - dispute_list_constraints: api_models::disputes::DisputeListConstraints, + dispute_list_constraints: &disputes::DisputeListConstraints, ) -> CustomResult, errors::DatabaseError> { let mut filter = ::table() .filter(dsl::merchant_id.eq(merchant_id.to_owned())) .order(dsl::modified_at.desc()) .into_boxed(); - if let Some(profile_id) = dispute_list_constraints.profile_id { - filter = filter.filter(dsl::profile_id.eq(profile_id)); + let mut search_by_payment_or_dispute_id = false; + + if let (Some(payment_id), Some(dispute_id)) = ( + &dispute_list_constraints.payment_id, + &dispute_list_constraints.dispute_id, + ) { + search_by_payment_or_dispute_id = true; + filter = filter + .filter(dsl::payment_id.eq(payment_id.to_owned())) + .or_filter(dsl::dispute_id.eq(dispute_id.to_owned())); + }; + + if !search_by_payment_or_dispute_id { + if let Some(payment_id) = &dispute_list_constraints.payment_id { + filter = filter.filter(dsl::payment_id.eq(payment_id.to_owned())); + }; } - if let Some(received_time) = dispute_list_constraints.received_time { - filter = filter.filter(dsl::created_at.eq(received_time)); + if !search_by_payment_or_dispute_id { + if let Some(dispute_id) = &dispute_list_constraints.dispute_id { + filter = filter.filter(dsl::dispute_id.eq(dispute_id.clone())); + }; } - if let Some(received_time_lt) = dispute_list_constraints.received_time_lt { - filter = filter.filter(dsl::created_at.lt(received_time_lt)); + + if let Some(time_range) = dispute_list_constraints.time_range { + filter = filter.filter(dsl::created_at.ge(time_range.start_time)); + + if let Some(end_time) = time_range.end_time { + filter = filter.filter(dsl::created_at.le(end_time)); + } } - if let Some(received_time_gt) = dispute_list_constraints.received_time_gt { - filter = filter.filter(dsl::created_at.gt(received_time_gt)); + + if let Some(profile_id) = &dispute_list_constraints.profile_id { + filter = filter.filter(dsl::profile_id.eq_any(profile_id.clone())); } - if let Some(received_time_lte) = dispute_list_constraints.received_time_lte { - filter = filter.filter(dsl::created_at.le(received_time_lte)); + if let Some(connector_list) = &dispute_list_constraints.connector { + filter = filter.filter(dsl::connector.eq_any(connector_list.clone())); } - if let Some(received_time_gte) = dispute_list_constraints.received_time_gte { - filter = filter.filter(dsl::created_at.ge(received_time_gte)); + + if let Some(reason) = &dispute_list_constraints.reason { + filter = filter.filter(dsl::connector_reason.eq(reason.clone())); } - if let Some(connector) = dispute_list_constraints.connector { - filter = filter.filter(dsl::connector.eq(connector)); + if let Some(dispute_stage) = &dispute_list_constraints.dispute_stage { + filter = filter.filter(dsl::dispute_stage.eq_any(dispute_stage.clone())); } - if let Some(reason) = dispute_list_constraints.reason { - filter = filter.filter(dsl::connector_reason.eq(reason)); + if let Some(dispute_status) = &dispute_list_constraints.dispute_status { + filter = filter.filter(dsl::dispute_status.eq_any(dispute_status.clone())); } - if let Some(dispute_stage) = dispute_list_constraints.dispute_stage { - filter = filter.filter(dsl::dispute_stage.eq(dispute_stage)); + + if let Some(currency_list) = &dispute_list_constraints.currency { + let currency: Vec = currency_list + .iter() + .map(|currency| currency.to_string()) + .collect(); + + filter = filter.filter(dsl::currency.eq_any(currency)); } - if let Some(dispute_status) = dispute_list_constraints.dispute_status { - filter = filter.filter(dsl::dispute_status.eq(dispute_status)); + if let Some(merchant_connector_id) = &dispute_list_constraints.merchant_connector_id { + filter = filter.filter(dsl::merchant_connector_id.eq(merchant_connector_id.clone())) } if let Some(limit) = dispute_list_constraints.limit { - filter = filter.limit(limit); + filter = filter.limit(limit.into()); + } + if let Some(offset) = dispute_list_constraints.offset { + filter = filter.offset(offset.into()); } logger::debug!(query = %diesel::debug_query::(&filter).to_string()); diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 12e30f54549..603c5cc91c1 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -274,6 +274,8 @@ pub enum Flow { DisputesRetrieve, /// Dispute List flow DisputesList, + /// Dispute Filters flow + DisputesFilters, /// Cards Info flow CardsInfo, /// Create File flow From 6d8992f905aec1376e958169729695b744fc39bf Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com> Date: Sun, 22 Sep 2024 23:40:52 +0530 Subject: [PATCH 45/48] fix(connector): [DEUTSCHE] Trim spaces in IBAN (#5978) --- .../src/connectors/deutschebank/transformers.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs index 8b3837edd63..30b1d65134a 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs @@ -155,7 +155,7 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>> Ok(Self::MandatePost(DeutschebankMandatePostRequest { approval_by: DeutschebankSEPAApproval::Click, email_address: item.router_data.request.get_email()?, - iban, + iban: Secret::from(iban.peek().replace(" ", "")), first_name: billing_address.get_first_name()?.clone(), last_name: billing_address.get_last_name()?.clone(), })) @@ -313,7 +313,7 @@ impl PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. - }) => Ok(iban), + }) => Ok(Secret::from(iban.peek().replace(" ", ""))), _ => Err(errors::ConnectorError::MissingRequiredField { field_name: "payment_method_data.bank_debit.sepa_bank_debit.iban" @@ -470,7 +470,7 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>> means_of_payment: DeutschebankMeansOfPayment { bank_account: DeutschebankBankAccount { account_holder, - iban, + iban: Secret::from(iban.peek().replace(" ", "")), }, }, mandate: { From 5a36252d3f6738367e065dd45764ac1eb3f7ea5e Mon Sep 17 00:00:00 2001 From: GORAKHNATH YADAV Date: Sun, 22 Sep 2024 23:42:18 +0530 Subject: [PATCH 46/48] docs: add terms and conditions (#5975) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 2 +- api-reference/openapi_spec.json | 2 +- crates/common_enums/src/enums.rs | 2 ++ docs/TERMS_OF_CONTEST.md | 23 +++++++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 docs/TERMS_OF_CONTEST.md diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 29dd0d3a96e..fb679970f3d 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -7314,7 +7314,7 @@ }, "FutureUsage": { "type": "string", - "description": "Indicates that you intend to make future payments with the payment methods used for this Payment. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.", + "description": "Indicates that you intend to make future payments with the payment methods used for this Payment. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.\n- On_session - Payment method saved only at hyperswitch when consent is provided by the user. CVV will asked during the returning user payment\n- Off_session - Payment method saved at both hyperswitch and Processor when consent is provided by the user. No input is required during the returning user payment.", "enum": [ "off_session", "on_session" diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 404d93f0d28..75e240cff7d 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -10732,7 +10732,7 @@ }, "FutureUsage": { "type": "string", - "description": "Indicates that you intend to make future payments with the payment methods used for this Payment. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.", + "description": "Indicates that you intend to make future payments with the payment methods used for this Payment. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.\n- On_session - Payment method saved only at hyperswitch when consent is provided by the user. CVV will asked during the returning user payment\n- Off_session - Payment method saved at both hyperswitch and Processor when consent is provided by the user. No input is required during the returning user payment.", "enum": [ "off_session", "on_session" diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 42e05b27cec..b5f26945e90 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1357,6 +1357,8 @@ pub enum IntentStatus { } /// Indicates that you intend to make future payments with the payment methods used for this Payment. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete. +/// - On_session - Payment method saved only at hyperswitch when consent is provided by the user. CVV will asked during the returning user payment +/// - Off_session - Payment method saved at both hyperswitch and Processor when consent is provided by the user. No input is required during the returning user payment. #[derive( Clone, Copy, diff --git a/docs/TERMS_OF_CONTEST.md b/docs/TERMS_OF_CONTEST.md new file mode 100644 index 00000000000..866c890bab3 --- /dev/null +++ b/docs/TERMS_OF_CONTEST.md @@ -0,0 +1,23 @@ +## Terms and Conditions + +Juspay Technologies Private Limited (Juspay) is the problem statement issuer for the contest and this contest is subject to the below stated terms and conditions. **Hyperswitch** is Juspay's open source full stack payment infrastructure solution to embrace diversity, reduce payment friction and manage compliance. + +By participating in this contest, Participants agree to the following terms and conditions: + +1. Eligibility: Participants must comply with all applicable laws and regulations. Juspay solely reserves the right to disqualify any participant or withhold prizes if participation or prize distribution if in Juspay's opinion the participant by its acts or omission violates or may violate any applicable laws. The discretionary rights for the same at all times shall remain with Juspay. + +2. Sanctions Compliance: Participants warrant that they are not located in any country subject to U.S. trade and economic sanctions, including but not limited to Cuba, Iran, North Korea, Syria, and the Crimea region of Ukraine. Participants also warrant that they are not listed on any U.S. list of prohibited parties, including the Treasury Department's List of Specially Designated Nationals (SDN List) or Sectoral Sanctions List (SSI List). + +3. Prize Distribution: Juspay will make reasonable efforts to distribute prizes to eligible winners. However, Juspay shall not be liable if prize distribution is impossible due to sanctions, export controls, or other legal restrictions. In such cases, the prize may be forfeited without any liability to Juspay. Juspay reserves the right to withhold any prize or disqualify any participant from the contest if in Juspay's opinion the participant has violated any rules of the contest. + +4. Participant Responsibility: Juspay is not responsible for any taxes, duties, or other costs associated with prize acceptance or use. + +5. Liability: To the fullest extent permitted by law, Juspay disclaims all liability for any issues arising from contest participation, including but not limited to inability to distribute prizes due to legal restrictions. Participants acknowledge that they enter the contest at their own risk and agree to indemnify and hold Juspay harmless from any claims, losses, liabilities or damages arising from their participation in the contest. + +6. Intellectual Property Rights: Juspay will solely retain ownership of the submissions by the participant. Juspay shall have the full right to use, modify, reproduce, distribute, and otherwise exploit the submission, in whole or in part, for any purpose whatsoever, without any additional permission of the participant.  + +7. Modifications: Juspay reserves the exclusive right to modify the terms and conditions, suspend, or terminate the contest at its sole discretion and at any time. + +8. Governing Law: These terms shall be governed by and construed in accordance with the laws of India, without regard to its conflict of law provisions and in the matters of any dispute between Juspay and the participant , the courts of Bangalore shall have exclusive jurisdiction.  + +9. By participating in this contest, Participant acknowledge that Participant have read, understood, and agree to be bound by these terms and conditions. \ No newline at end of file From d3192334e8bc72d9ccdb12375d513db37aef63f0 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:20:53 +0000 Subject: [PATCH 47/48] chore(postman): update Postman collection files --- .../nmi.postman_collection.json | 341 ++++++++++++------ 1 file changed, 240 insertions(+), 101 deletions(-) diff --git a/postman/collection-json/nmi.postman_collection.json b/postman/collection-json/nmi.postman_collection.json index 054d66c4fd2..4e75f9f57ea 100644 --- a/postman/collection-json/nmi.postman_collection.json +++ b/postman/collection-json/nmi.postman_collection.json @@ -6618,6 +6618,115 @@ }, "response": [] }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\"", + " );", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + }, { "name": "List payment methods for a Customer", "event": [ @@ -6997,47 +7106,62 @@ "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" }, "response": [] - }, + } + ] + }, + { + "name": "Scenario11-Don't Pass CVV for save card flow and verify success payment", + "item": [ { - "name": "Payments - Retrieve", + "name": "Payments - Create", "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.environment.set(\"random_number\", _.random(100, 100000));", + "" + ], + "type": "text/javascript" + } + }, { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", " pm.response.to.be.success;", "});", "", "// Validate if response header has matching content-type", - "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", + " \"application/json\"", " );", "});", "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", "// Set response object as internal variable", "let jsonData = {};", "try {", " jsonData = pm.response.json();", "} catch (e) {}", "", - "// Validate if response has JSON Body", - "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", - "});", - "", "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", "if (jsonData?.payment_id) {", " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", " console.log(", " \"- use {{payment_id}} as collection variable for value\",", - " jsonData.payment_id,", + " jsonData.payment_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", " );", "}", "", @@ -7046,11 +7170,11 @@ " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", " console.log(", " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " jsonData.mandate_id", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", " );", "}", "", @@ -7059,11 +7183,33 @@ " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", " console.log(", " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " jsonData.client_secret", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " }", + " );", + "}", + "", + "if (jsonData?.customer_id) {", + " pm.collectionVariables.set(\"customer_id\", jsonData.customer_id);", + " console.log(", + " \"- use {{customer_id}} as collection variable for value\",", + " jsonData.customer_id", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{customer_id}}, as jsonData.customer_id is undefined.\"", " );", "}", "" @@ -7073,152 +7219,145 @@ } ], "request": { - "method": "GET", + "method": "POST", "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, { "key": "Accept", "value": "application/json" } ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"stripesavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + }, "url": { - "raw": "{{baseUrl}}/payments/:id", + "raw": "{{baseUrl}}/payments", "host": [ "{{baseUrl}}" ], "path": [ - "payments", - ":id" - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } + "payments" ] }, - "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" }, "response": [] - } - ] - }, - { - "name": "Scenario11-Don't Pass CVV for save card flow and verify success payment", - "item": [ + }, { - "name": "Payments - Create", + "name": "Payments - Retrieve", "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "pm.environment.set(\"random_number\", _.random(100, 100000));", - "" - ], - "type": "text/javascript" - } - }, { "listen": "test", "script": { "exec": [ - "// Validate status 2xx ", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", - " pm.response.to.be.success;", + "// Validate status 2xx", + "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", "});", "", "// Validate if response header has matching content-type", - "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(\"application/json\");", - "});", - "", - "// Validate if response has JSON Body ", - "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", - " pm.response.to.have.jsonBody();", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\"", + " );", "});", "", "// Set response object as internal variable", "let jsonData = {};", - "try {jsonData = pm.response.json();}catch(e){}", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", "", "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", "if (jsonData?.payment_id) {", - " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", - " console.log(\"- use {{payment_id}} as collection variable for value\",jsonData.payment_id);", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id", + " );", "} else {", - " console.log('INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.');", - "};", - "", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\"", + " );", + "}", "", "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", "if (jsonData?.mandate_id) {", - " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", - " console.log(\"- use {{mandate_id}} as collection variable for value\",jsonData.mandate_id);", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id", + " );", "} else {", - " console.log('INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.');", - "};", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\"", + " );", + "}", "", "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", - " console.log(\"- use {{client_secret}} as collection variable for value\",jsonData.client_secret);", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret", + " );", "} else {", - " console.log('INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.');", - "};", - "", - "// Response body should have value \"processing\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"processing\");", - " },", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\"", " );", "}", - "", - "if (jsonData?.customer_id) {", - " pm.collectionVariables.set(\"customer_id\", jsonData.customer_id);", - " console.log(\"- use {{customer_id}} as collection variable for value\",jsonData.customer_id);", - "} else {", - " console.log('INFO - Unable to assign variable {{customer_id}}, as jsonData.customer_id is undefined.');", - "};" + "" ], "type": "text/javascript" } } ], "request": { - "method": "POST", + "method": "GET", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "options": { - "raw": { - "language": "json" - } - }, - "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"stripesavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" - }, "url": { - "raw": "{{baseUrl}}/payments", + "raw": "{{baseUrl}}/payments/:id?force_sync=true", "host": [ "{{baseUrl}}" ], "path": [ - "payments" + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } ] }, - "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" }, "response": [] }, From 5fec99b58b0062dbc02fc8fdc1bd8d949d76c51e Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:25:14 +0000 Subject: [PATCH 48/48] chore(version): 2024.09.23.0 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d43b35d96bb..46ac76cf7ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,38 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.09.23.0 + +### Features + +- **connector:** [DEUTSCHEBANK] Implement SEPA recurring payments ([#5925](https://github.com/juspay/hyperswitch/pull/5925)) ([`00e913c`](https://github.com/juspay/hyperswitch/commit/00e913c75c14a45fdd513b233f67db7edbaf7380)) +- **disputes:** Add filters for disputes list ([#5637](https://github.com/juspay/hyperswitch/pull/5637)) ([`365f568`](https://github.com/juspay/hyperswitch/commit/365f5680b77023865b21364a14c21349075aef19)) +- **router:** Add support for Samsung Pay payment method ([#5955](https://github.com/juspay/hyperswitch/pull/5955)) ([`fe15cc7`](https://github.com/juspay/hyperswitch/commit/fe15cc79f57684b928f5413a9b928ea5b4071b92)) + +### Bug Fixes + +- **connector:** [DEUTSCHE] Trim spaces in IBAN ([#5978](https://github.com/juspay/hyperswitch/pull/5978)) ([`6d8992f`](https://github.com/juspay/hyperswitch/commit/6d8992f905aec1376e958169729695b744fc39bf)) +- **external_services:** Add proto build under the dynamic_routing feature flag ([#5945](https://github.com/juspay/hyperswitch/pull/5945)) ([`c0cac8d`](https://github.com/juspay/hyperswitch/commit/c0cac8d9135b14945ce5763327ec16b1578ca2a2)) +- Do not allow duplicate organization name ([#5919](https://github.com/juspay/hyperswitch/pull/5919)) ([`c8f7232`](https://github.com/juspay/hyperswitch/commit/c8f7232a3001be1fc5d8b0fedfd703030df83789)) + +### Refactors + +- **open_banking:** Refactored to consume and use headers from SDK ([#5884](https://github.com/juspay/hyperswitch/pull/5884)) ([`d9270ac`](https://github.com/juspay/hyperswitch/commit/d9270ace8ddde16eca8c45ceb79af3e4d815d7cd)) +- **payment_intent_v2:** Payment intent fields refactoring ([#5880](https://github.com/juspay/hyperswitch/pull/5880)) ([`5335f2d`](https://github.com/juspay/hyperswitch/commit/5335f2d21ce6f0c11dc84fd56b3cd2c80e8b064f)) +- **router:** Add `email` in `billing` and `shipping` address of merchant payment method list ([#5981](https://github.com/juspay/hyperswitch/pull/5981)) ([`3792562`](https://github.com/juspay/hyperswitch/commit/37925626e6446900f1d16e0e5f184ee472d4be3e)) + +### Documentation + +- Add terms and conditions ([#5975](https://github.com/juspay/hyperswitch/pull/5975)) ([`5a36252`](https://github.com/juspay/hyperswitch/commit/5a36252d3f6738367e065dd45764ac1eb3f7ea5e)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`d319233`](https://github.com/juspay/hyperswitch/commit/d3192334e8bc72d9ccdb12375d513db37aef63f0)) + +**Full Changelog:** [`2024.09.20.0...2024.09.23.0`](https://github.com/juspay/hyperswitch/compare/2024.09.20.0...2024.09.23.0) + +- - - + ## 2024.09.20.0 ### Features