From 3b10b1c473209e36183271a81eb9014a8f5cddfa Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:46:35 +0530 Subject: [PATCH 01/11] fix(CI): fix spell check for CI pull request (#2420) --- crates/router/tests/connectors/authorizedotnet.rs | 14 +++++++------- crates/router/tests/connectors/cybersource.rs | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/router/tests/connectors/authorizedotnet.rs b/crates/router/tests/connectors/authorizedotnet.rs index 125851d0bea1..4750e122a217 100644 --- a/crates/router/tests/connectors/authorizedotnet.rs +++ b/crates/router/tests/connectors/authorizedotnet.rs @@ -512,31 +512,31 @@ async fn should_fail_capture_for_invalid_payment() { } #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_partially_refund_manually_captured_payment() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_refund_manually_captured_payment() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_sync_manually_captured_refund() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_refund_auto_captured_payment() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_partially_refund_succeeded_payment() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_refund_succeeded_payment_multiple_times() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_fail_for_refund_amount_higher_than_payment_amount() {} // Connector dependent test cases goes here diff --git a/crates/router/tests/connectors/cybersource.rs b/crates/router/tests/connectors/cybersource.rs index bcc5aaa2a209..5f9adf39f9b9 100644 --- a/crates/router/tests/connectors/cybersource.rs +++ b/crates/router/tests/connectors/cybersource.rs @@ -314,7 +314,7 @@ async fn should_partially_refund_succeeded_payment() { } #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_partially_refund_manually_captured_payment() { let connector = Cybersource {}; let response = connector @@ -373,17 +373,17 @@ async fn should_sync_refund() { } #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_sync_manually_captured_refund() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_refund_auto_captured_payment() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_refund_succeeded_payment_multiple_times() {} #[actix_web::test] -#[ignore = "refunds tests are ignored for this connector becuase it takes one day for a payment to be settled."] +#[ignore = "refunds tests are ignored for this connector because it takes one day for a payment to be settled."] async fn should_fail_for_refund_amount_higher_than_payment_amount() {} From 6bf0e75b69608ea07fd7601906982a19cdc81400 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 12:52:38 +0530 Subject: [PATCH 02/11] chore(deps): bump webpki from 0.22.0 to 0.22.2 (#2419) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0ad241cbe0b..cca3b6f58d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5842,9 +5842,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" dependencies = [ "ring", "untrusted", @@ -6146,4 +6146,4 @@ dependencies = [ "cc", "libc", "pkg-config", -] \ No newline at end of file +] From f12ce9c72d94674e0ae0ec7f1c91d8b5c43481e8 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Tue, 3 Oct 2023 12:53:40 +0530 Subject: [PATCH 03/11] fix(payment_methos): prioritized `apple_pay_combined` deserialization over `apple_pay` (#2393) --- crates/api_models/src/payments.rs | 8 +++- .../src/connector/bluesnap/transformers.rs | 46 ++++++++++++++++--- crates/router/src/core/payments.rs | 28 +++++++++-- .../src/core/payments/flows/session_flow.rs | 25 ++++++++-- 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 6a06b76d0e47..6711f202e976 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2375,8 +2375,12 @@ pub struct ApplepayConnectorMetadataRequest { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ApplepaySessionTokenData { - #[serde(flatten)] - pub data: ApplepaySessionTokenMetadata, + pub apple_pay: ApplePayMetadata, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct ApplepayCombinedSessionTokenData { + pub apple_pay_combined: ApplePayCombinedMetadata, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/crates/router/src/connector/bluesnap/transformers.rs b/crates/router/src/connector/bluesnap/transformers.rs index fc9238f84e87..fe92c337a012 100644 --- a/crates/router/src/connector/bluesnap/transformers.rs +++ b/crates/router/src/connector/bluesnap/transformers.rs @@ -423,11 +423,28 @@ impl TryFrom<&types::PaymentsSessionRouterData> for BluesnapCreateWalletToken { fn try_from(item: &types::PaymentsSessionRouterData) -> Result { let apple_pay_metadata = item.get_connector_meta()?.expose(); let applepay_metadata = apple_pay_metadata - .parse_value::( - "ApplepaySessionTokenData", + .clone() + .parse_value::( + "ApplepayCombinedSessionTokenData", ) + .map(|combined_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + combined_metadata.apple_pay_combined, + ) + }) + .or_else(|_| { + apple_pay_metadata + .parse_value::( + "ApplepaySessionTokenData", + ) + .map(|old_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePay( + old_metadata.apple_pay, + ) + }) + }) .change_context(errors::ConnectorError::ParsingFailed)?; - let session_token_data = match applepay_metadata.data { + let session_token_data = match applepay_metadata { payments::ApplepaySessionTokenMetadata::ApplePay(apple_pay_data) => { Ok(apple_pay_data.session_token_data) } @@ -468,12 +485,29 @@ impl TryFrom( - "ApplepaySessionTokenData", + .clone() + .parse_value::( + "ApplepayCombinedSessionTokenData", ) + .map(|combined_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + combined_metadata.apple_pay_combined, + ) + }) + .or_else(|_| { + metadata + .parse_value::( + "ApplepaySessionTokenData", + ) + .map(|old_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePay( + old_metadata.apple_pay, + ) + }) + }) .change_context(errors::ConnectorError::ParsingFailed)?; - let (payment_request_data, session_token_data) = match applepay_metadata.data { + let (payment_request_data, session_token_data) = match applepay_metadata { payments::ApplepaySessionTokenMetadata::ApplePayCombined(_apple_pay_combined) => { Err(errors::ConnectorError::FlowNotSupported { flow: "apple pay combined".to_string(), diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 170a780e701d..cb2b24c4158c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1127,10 +1127,32 @@ fn check_apple_pay_metadata( merchant_connector_account.clone().and_then(|mca| { let metadata = mca.get_metadata(); metadata.and_then(|apple_pay_metadata| { - let parsed_metadata: Result = - apple_pay_metadata.parse_value("ApplepaySessionTokenData").map_err(|error| logger::error!(%error, "Failed to Parse Value to ApplepaySessionTokenData")); + let parsed_metadata = apple_pay_metadata + .clone() + .parse_value::( + "ApplepayCombinedSessionTokenData", + ) + .map(|combined_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + combined_metadata.apple_pay_combined, + ) + }) + .or_else(|_| { + apple_pay_metadata + .parse_value::( + "ApplepaySessionTokenData", + ) + .map(|old_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePay( + old_metadata.apple_pay, + ) + }) + }) + .map_err( + |error| logger::error!(%error, "Failed to Parse Value to ApplepaySessionTokenData"), + ); - parsed_metadata.ok().map(|metadata| match metadata.data { + parsed_metadata.ok().map(|metadata| match metadata { api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( apple_pay_combined, ) => match apple_pay_combined { diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 8b8ae990c0ad..a6ae83ac1572 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -87,9 +87,28 @@ impl Feature for types::PaymentsSessio fn get_applepay_metadata( connector_metadata: Option, -) -> RouterResult { +) -> RouterResult { connector_metadata - .parse_value::("ApplepaySessionTokenData") + .clone() + .parse_value::( + "ApplepayCombinedSessionTokenData", + ) + .map(|combined_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined( + combined_metadata.apple_pay_combined, + ) + }) + .or_else(|_| { + connector_metadata + .parse_value::( + "ApplepaySessionTokenData", + ) + .map(|old_metadata| { + api_models::payments::ApplepaySessionTokenMetadata::ApplePay( + old_metadata.apple_pay, + ) + }) + }) .change_context(errors::ApiErrorResponse::InvalidDataFormat { field_name: "connector_metadata".to_string(), expected_format: "applepay_metadata_format".to_string(), @@ -154,7 +173,7 @@ async fn create_applepay_session_token( apple_pay_session_request, apple_pay_merchant_cert, apple_pay_merchant_cert_key, - ) = match apple_pay_metadata.data { + ) = match apple_pay_metadata { payment_types::ApplepaySessionTokenMetadata::ApplePayCombined( apple_pay_combined_metadata, ) => match apple_pay_combined_metadata { From abfdea20b06a8804ec83fe9431f9a034465bb924 Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:07:43 +0530 Subject: [PATCH 04/11] feat(pm_auth): Added pm_auth_config to merchant_connector_account (#2183) Co-authored-by: Sarthak Soni --- crates/api_models/src/admin.rs | 6 ++++++ crates/common_enums/src/enums.rs | 2 ++ .../diesel_models/src/merchant_connector_account.rs | 4 ++++ crates/diesel_models/src/schema.rs | 1 + crates/router/src/core/admin.rs | 2 ++ crates/router/src/core/verification/utils.rs | 1 + crates/router/src/db/merchant_connector_account.rs | 2 ++ .../src/types/domain/merchant_connector_account.rs | 7 +++++++ crates/router/src/types/transformers.rs | 1 + .../down.sql | 2 ++ .../2023-09-18-104900_add_pm_auth_config_mca/up.sql | 3 +++ openapi/openapi_spec.json | 12 +++++++++++- 12 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 migrations/2023-09-18-104900_add_pm_auth_config_mca/down.sql create mode 100644 migrations/2023-09-18-104900_add_pm_auth_config_mca/up.sql diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 94d7c5b4a8bc..a1b13c5ff315 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -633,6 +633,8 @@ pub struct MerchantConnectorCreate { pub connector_webhook_details: Option, /// Identifier for the business profile, if not provided default will be chosen from merchant account pub profile_id: Option, + + pub pm_auth_config: Option, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] @@ -736,6 +738,8 @@ pub struct MerchantConnectorResponse { pub profile_id: Option, /// identifier for the verified domains of a particular connector account pub applepay_verified_domains: Option>, + + pub pm_auth_config: 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." @@ -805,6 +809,8 @@ pub struct MerchantConnectorUpdate { } }))] pub connector_webhook_details: Option, + + pub pm_auth_config: Option, } ///Details of FrmConfigs are mentioned here... it should be passed in payment connector create api call, and stored in merchant_connector_table diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 779a4f05dd73..c693ea57fc58 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -208,6 +208,8 @@ pub enum ConnectorType { NonBankingFinance, /// Acquirers, Gateways etc PayoutProcessor, + /// PaymentMethods Auth Services + PaymentMethodAuth, } #[allow(clippy::upper_case_acronyms)] diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index cd5b66f523a0..ce2684c937ab 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -41,6 +41,7 @@ pub struct MerchantConnectorAccount { pub profile_id: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, } #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] @@ -68,6 +69,7 @@ pub struct MerchantConnectorAccountNew { pub profile_id: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, } #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] @@ -89,6 +91,7 @@ pub struct MerchantConnectorAccountUpdateInternal { pub frm_config: Option>>, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, } impl MerchantConnectorAccountUpdateInternal { @@ -110,6 +113,7 @@ impl MerchantConnectorAccountUpdateInternal { payment_methods_enabled: self.payment_methods_enabled, frm_config: self.frm_config, modified_at: self.modified_at.unwrap_or(source.modified_at), + pm_auth_config: self.pm_auth_config, ..source } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 8d84475ddd45..3a6e2cd2a936 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -476,6 +476,7 @@ diesel::table! { #[max_length = 64] profile_id -> Nullable, applepay_verified_domains -> Nullable>>, + pm_auth_config -> Nullable, } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index a6847968c286..fab416c431ba 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -682,6 +682,7 @@ pub async fn create_payment_connector( }, profile_id: Some(profile_id.clone()), applepay_verified_domains: None, + pm_auth_config: req.pm_auth_config.clone(), }; let mca = state @@ -847,6 +848,7 @@ pub async fn update_payment_connector( None => None, }, applepay_verified_domains: None, + pm_auth_config: req.pm_auth_config, }; let updated_mca = db diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index 36bf4b7d7167..056cc8c2e54f 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -58,6 +58,7 @@ pub async fn check_existence_and_add_domain_to_db( frm_configs: None, connector_webhook_details: None, applepay_verified_domains: Some(already_verified_domains.clone()), + pm_auth_config: None, }; state .store diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index e6377ea265bc..491e5b788103 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -650,6 +650,7 @@ impl MerchantConnectorAccountInterface for MockDb { connector_webhook_details: t.connector_webhook_details, profile_id: t.profile_id, applepay_verified_domains: t.applepay_verified_domains, + pm_auth_config: t.pm_auth_config, }; accounts.push(account.clone()); account @@ -845,6 +846,7 @@ mod merchant_connector_account_cache_tests { connector_webhook_details: None, profile_id: Some(profile_id.to_string()), applepay_verified_domains: None, + pm_auth_config: None, }; db.insert_merchant_connector_account(mca.clone(), &merchant_key) diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs index 7c23890345de..a8dce1e6f078 100644 --- a/crates/router/src/types/domain/merchant_connector_account.rs +++ b/crates/router/src/types/domain/merchant_connector_account.rs @@ -34,6 +34,7 @@ pub struct MerchantConnectorAccount { pub connector_webhook_details: Option, pub profile_id: Option, pub applepay_verified_domains: Option>, + pub pm_auth_config: Option, } #[derive(Debug)] @@ -51,6 +52,7 @@ pub enum MerchantConnectorAccountUpdate { frm_configs: Option>>, connector_webhook_details: Option, applepay_verified_domains: Option>, + pm_auth_config: Option, }, } @@ -85,6 +87,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { connector_webhook_details: self.connector_webhook_details, profile_id: self.profile_id, applepay_verified_domains: self.applepay_verified_domains, + pm_auth_config: self.pm_auth_config, }, ) } @@ -123,6 +126,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { connector_webhook_details: other.connector_webhook_details, profile_id: other.profile_id, applepay_verified_domains: other.applepay_verified_domains, + pm_auth_config: other.pm_auth_config, }) } @@ -149,6 +153,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { connector_webhook_details: self.connector_webhook_details, profile_id: self.profile_id, applepay_verified_domains: self.applepay_verified_domains, + pm_auth_config: self.pm_auth_config, }) } } @@ -169,6 +174,7 @@ impl From for MerchantConnectorAccountUpdateInte frm_configs, connector_webhook_details, applepay_verified_domains, + pm_auth_config, } => Self { merchant_id, connector_type, @@ -184,6 +190,7 @@ impl From for MerchantConnectorAccountUpdateInte modified_at: Some(common_utils::date_time::now()), connector_webhook_details, applepay_verified_domains, + pm_auth_config, }, } } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 5bdb63814e84..f114bf38b62e 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -668,6 +668,7 @@ impl TryFrom for api_models::admin::MerchantCo .transpose()?, profile_id: item.profile_id, applepay_verified_domains: item.applepay_verified_domains, + pm_auth_config: item.pm_auth_config, }) } } diff --git a/migrations/2023-09-18-104900_add_pm_auth_config_mca/down.sql b/migrations/2023-09-18-104900_add_pm_auth_config_mca/down.sql new file mode 100644 index 000000000000..d4fc2b581edf --- /dev/null +++ b/migrations/2023-09-18-104900_add_pm_auth_config_mca/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS pm_auth_config; \ No newline at end of file diff --git a/migrations/2023-09-18-104900_add_pm_auth_config_mca/up.sql b/migrations/2023-09-18-104900_add_pm_auth_config_mca/up.sql new file mode 100644 index 000000000000..15cdafdbaccf --- /dev/null +++ b/migrations/2023-09-18-104900_add_pm_auth_config_mca/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE merchant_connector_account ADD COLUMN IF NOT EXISTS pm_auth_config JSONB DEFAULT NULL; +ALTER TYPE "ConnectorType" ADD VALUE 'payment_method_auth'; \ No newline at end of file diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 2bce9585fdf4..1d943117c8e9 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -3922,7 +3922,8 @@ "networks", "banking_entities", "non_banking_finance", - "payout_processor" + "payout_processor", + "payment_method_auth" ] }, "CountryAlpha2": { @@ -6522,6 +6523,9 @@ "type": "string", "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", "nullable": true + }, + "pm_auth_config": { + "nullable": true } } }, @@ -6746,6 +6750,9 @@ }, "description": "identifier for the verified domains of a particular connector account", "nullable": true + }, + "pm_auth_config": { + "nullable": true } } }, @@ -6843,6 +6850,9 @@ } ], "nullable": true + }, + "pm_auth_config": { + "nullable": true } } }, From 5048d248e59b8ecaf8585ffd5134953cf62e74ef Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:57:34 +0530 Subject: [PATCH 05/11] feat(webhooks): webhooks effect tracker (#2260) --- crates/api_models/src/webhooks.rs | 22 ++++- crates/router/src/core/webhooks.rs | 105 +++++++++++++++++------ crates/router/src/core/webhooks/utils.rs | 3 + crates/router/src/routes/webhooks.rs | 4 +- 4 files changed, 103 insertions(+), 31 deletions(-) diff --git a/crates/api_models/src/webhooks.rs b/crates/api_models/src/webhooks.rs index 94274344abfd..a1cafee1f8d9 100644 --- a/crates/api_models/src/webhooks.rs +++ b/crates/api_models/src/webhooks.rs @@ -5,7 +5,7 @@ use utoipa::ToSchema; use crate::{disputes, enums as api_enums, payments, refunds}; -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)] #[serde(rename_all = "snake_case")] pub enum IncomingWebhookEvent { PaymentIntentFailure, @@ -39,6 +39,26 @@ pub enum WebhookFlow { BankTransfer, } +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +/// This enum tells about the affect a webhook had on an object +pub enum WebhookResponseTracker { + Payment { + payment_id: String, + status: common_enums::IntentStatus, + }, + Refund { + payment_id: String, + refund_id: String, + status: common_enums::RefundStatus, + }, + Dispute { + dispute_id: String, + payment_id: String, + status: common_enums::DisputeStatus, + }, + NoEffect, +} + impl From for WebhookFlow { fn from(evt: IncomingWebhookEvent) -> Self { match evt { diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index c365e3e6bf51..6a33c97e3914 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -3,7 +3,7 @@ pub mod utils; use std::str::FromStr; -use api_models::payments::HeaderPayload; +use api_models::{payments::HeaderPayload, webhooks::WebhookResponseTracker}; use common_utils::errors::ReportSwitchExt; use error_stack::{report, IntoReport, ResultExt}; use masking::ExposeInterface; @@ -40,7 +40,7 @@ pub async fn payments_incoming_webhook_flow( key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, -) -> CustomResult<(), errors::ApiErrorResponse> { +) -> CustomResult { let consume_or_trigger_flow = if source_verified { payments::CallConnectorAction::HandleResponse(webhook_details.resource_object) } else { @@ -111,9 +111,12 @@ pub async fn payments_incoming_webhook_flow( metrics::WEBHOOK_PAYMENT_NOT_FOUND.add( &metrics::CONTEXT, 1, - &[add_attributes("merchant_id", merchant_account.merchant_id)], + &[add_attributes( + "merchant_id", + merchant_account.merchant_id.clone(), + )], ); - return Ok(()); + return Ok(WebhookResponseTracker::NoEffect); } error @ Err(_) => error?, } @@ -134,6 +137,8 @@ pub async fn payments_incoming_webhook_flow( .change_context(errors::ApiErrorResponse::WebhookProcessingFailure) .attach_printable("payment id not received from payments core")?; + let status = payments_response.status; + let event_type: Option = payments_response.status.foreign_into(); // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook @@ -144,20 +149,22 @@ pub async fn payments_incoming_webhook_flow( outgoing_event_type, enums::EventClass::Payments, None, - payment_id, + payment_id.clone(), enums::EventObjectType::PaymentDetails, api::OutgoingWebhookContent::PaymentDetails(payments_response), ) .await?; - } + }; + + let response = WebhookResponseTracker::Payment { payment_id, status }; + + Ok(response) } _ => Err(errors::ApiErrorResponse::WebhookProcessingFailure) .into_report() .attach_printable("received non-json response from payments core")?, } - - Ok(()) } #[instrument(skip_all)] @@ -169,7 +176,7 @@ pub async fn refunds_incoming_webhook_flow( connector_name: &str, source_verified: bool, event_type: api_models::webhooks::IncomingWebhookEvent, -) -> CustomResult<(), errors::ApiErrorResponse> { +) -> CustomResult { let db = &*state.store; //find refund by connector refund id let refund = match webhook_details.object_reference_id { @@ -246,7 +253,8 @@ pub async fn refunds_incoming_webhook_flow( // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook if let Some(outgoing_event_type) = event_type { - let refund_response: api_models::refunds::RefundResponse = updated_refund.foreign_into(); + let refund_response: api_models::refunds::RefundResponse = + updated_refund.clone().foreign_into(); create_event_and_trigger_outgoing_webhook::( state, merchant_account, @@ -260,7 +268,11 @@ pub async fn refunds_incoming_webhook_flow( .await?; } - Ok(()) + Ok(WebhookResponseTracker::Refund { + payment_id: updated_refund.payment_id, + refund_id: updated_refund.refund_id, + status: updated_refund.refund_status, + }) } pub async fn get_payment_attempt_from_object_reference_id( @@ -386,7 +398,7 @@ pub async fn disputes_incoming_webhook_flow( connector: &(dyn api::Connector + Sync), request_details: &api::IncomingWebhookRequestDetails<'_>, event_type: api_models::webhooks::IncomingWebhookEvent, -) -> CustomResult<(), errors::ApiErrorResponse> { +) -> CustomResult { metrics::INCOMING_DISPUTE_WEBHOOK_METRIC.add(&metrics::CONTEXT, 1, &[]); if source_verified { let db = &*state.store; @@ -411,7 +423,7 @@ pub async fn disputes_incoming_webhook_flow( dispute_details, &merchant_account.merchant_id, &payment_attempt, - event_type.clone(), + event_type, connector.id(), ) .await?; @@ -424,13 +436,17 @@ pub async fn disputes_incoming_webhook_flow( event_type, enums::EventClass::Disputes, None, - dispute_object.dispute_id, + dispute_object.dispute_id.clone(), enums::EventObjectType::DisputeDetails, api::OutgoingWebhookContent::DisputeDetails(disputes_response), ) .await?; metrics::INCOMING_DISPUTE_WEBHOOK_MERCHANT_NOTIFIED_METRIC.add(&metrics::CONTEXT, 1, &[]); - Ok(()) + Ok(WebhookResponseTracker::Dispute { + dispute_id: dispute_object.dispute_id, + payment_id: dispute_object.payment_id, + status: dispute_object.dispute_status, + }) } else { metrics::INCOMING_DISPUTE_WEBHOOK_SIGNATURE_FAILURE_METRIC.add(&metrics::CONTEXT, 1, &[]); Err(errors::ApiErrorResponse::WebhookAuthenticationFailed).into_report() @@ -443,7 +459,7 @@ async fn bank_transfer_webhook_flow( key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, -) -> CustomResult<(), errors::ApiErrorResponse> { +) -> CustomResult { let response = if source_verified { let payment_attempt = get_payment_attempt_from_object_reference_id( &state, @@ -486,6 +502,7 @@ async fn bank_transfer_webhook_flow( .attach_printable("did not receive payment id from payments core response")?; let event_type: Option = payments_response.status.foreign_into(); + let status = payments_response.status; // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook if let Some(outgoing_event_type) = event_type { @@ -495,20 +512,20 @@ async fn bank_transfer_webhook_flow( outgoing_event_type, enums::EventClass::Payments, None, - payment_id, + payment_id.clone(), enums::EventObjectType::PaymentDetails, api::OutgoingWebhookContent::PaymentDetails(payments_response), ) .await?; } + + Ok(WebhookResponseTracker::Payment { payment_id, status }) } _ => Err(errors::ApiErrorResponse::WebhookProcessingFailure) .into_report() .attach_printable("received non-json response from payments core")?, } - - Ok(()) } #[allow(clippy::too_many_arguments)] @@ -729,6 +746,27 @@ pub async fn trigger_webhook_to_merchant( Ok(()) } +pub async fn webhooks_wrapper( + state: AppState, + req: &actix_web::HttpRequest, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + connector_name_or_mca_id: &str, + body: actix_web::web::Bytes, +) -> RouterResponse { + let (application_response, _webhooks_response_tracker) = webhooks_core::( + state, + req, + merchant_account, + key_store, + connector_name_or_mca_id, + body, + ) + .await?; + + Ok(application_response) +} + #[instrument(skip_all)] pub async fn webhooks_core( state: AppState, @@ -737,7 +775,10 @@ pub async fn webhooks_core( key_store: domain::MerchantKeyStore, connector_name_or_mca_id: &str, body: actix_web::web::Bytes, -) -> RouterResponse { +) -> errors::RouterResult<( + services::ApplicationResponse, + WebhookResponseTracker, +)> { metrics::WEBHOOK_INCOMING_COUNT.add( &metrics::CONTEXT, 1, @@ -754,8 +795,11 @@ pub async fn webhooks_core( body: &body, }; + // Fetch the merchant connector account to get the webhooks source secret + // `webhooks source secret` is a secret shared between the merchant and connector + // This is used for source verification and webhooks integrity let (merchant_connector_account, connector) = fetch_mca_and_connector( - state.clone(), + &state, &merchant_account, connector_name_or_mca_id, &key_store, @@ -810,10 +854,12 @@ pub async fn webhooks_core( ], ); - return connector + let response = connector .get_webhook_api_response(&request_details) .switch() - .attach_printable("Failed while early return in case of event type parsing"); + .attach_printable("Failed while early return in case of event type parsing")?; + + return Ok((response, WebhookResponseTracker::NoEffect)); } }; @@ -829,7 +875,9 @@ pub async fn webhooks_core( logger::info!(event_type=?event_type); let flow_type: api::WebhookFlow = event_type.to_owned().into(); - if process_webhook_further && !matches!(flow_type, api::WebhookFlow::ReturnResponse) { + let webhook_effect = if process_webhook_further + && !matches!(flow_type, api::WebhookFlow::ReturnResponse) + { let object_ref_id = connector .get_webhook_object_reference_id(&request_details) .switch() @@ -962,7 +1010,7 @@ pub async fn webhooks_core( .await .attach_printable("Incoming bank-transfer webhook flow failed")?, - api::WebhookFlow::ReturnResponse => {} + api::WebhookFlow::ReturnResponse => WebhookResponseTracker::NoEffect, _ => Err(errors::ApiErrorResponse::InternalServerError) .into_report() @@ -977,14 +1025,15 @@ pub async fn webhooks_core( merchant_account.merchant_id.clone(), )], ); - } + WebhookResponseTracker::NoEffect + }; let response = connector .get_webhook_api_response(&request_details) .switch() .attach_printable("Could not get incoming webhook api response from connector")?; - Ok(response) + Ok((response, webhook_effect)) } #[inline] @@ -1026,7 +1075,7 @@ pub async fn get_payment_id( } async fn fetch_mca_and_connector( - state: AppState, + state: &AppState, merchant_account: &domain::MerchantAccount, connector_name_or_mca_id: &str, key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index 33fa9291670e..05aafd872e3a 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -29,6 +29,9 @@ const IRRELEVANT_ATTEMPT_ID_IN_SOURCE_VERIFICATION_FLOW: &str = const IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_SOURCE_VERIFICATION_FLOW: &str = "irrelevant_connector_request_reference_id_in_source_verification_flow"; +/// Check whether the merchant has configured to process the webhook `event` for the `connector` +/// First check for the key "whconf_{merchant_id}_{connector_id}" in redis, +/// if not found, fetch from configs table in database, if not found use default pub async fn lookup_webhook_event( db: &dyn StorageInterface, connector_id: &str, diff --git a/crates/router/src/routes/webhooks.rs b/crates/router/src/routes/webhooks.rs index fa28e3fa7fb5..0bbc6add4361 100644 --- a/crates/router/src/routes/webhooks.rs +++ b/crates/router/src/routes/webhooks.rs @@ -26,8 +26,8 @@ pub async fn receive_incoming_webhook( &req, body, |state, auth, body| { - webhooks::webhooks_core::( - state, + webhooks::webhooks_wrapper::( + state.to_owned(), &req, auth.merchant_account, auth.key_store, From 36805411772da00719a716d05c650f10ca990d49 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa <70575890+prasunna09@users.noreply.github.com> Date: Tue, 3 Oct 2023 18:00:21 +0530 Subject: [PATCH 06/11] refactor(connector): [Cryptopay] Update PSync with connector_request_reference_id (#2388) --- crates/router/src/connector/cryptopay.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/router/src/connector/cryptopay.rs b/crates/router/src/connector/cryptopay.rs index fd324fcc68b8..2387a9bf4019 100644 --- a/crates/router/src/connector/cryptopay.rs +++ b/crates/router/src/connector/cryptopay.rs @@ -295,16 +295,10 @@ impl ConnectorIntegration CustomResult { - let connector_id = req - .request - .connector_transaction_id - .get_connector_transaction_id() - .change_context(errors::ConnectorError::MissingConnectorTransactionID)?; - + let custom_id = req.connector_request_reference_id.clone(); Ok(format!( - "{}/api/invoices/{}", - self.base_url(connectors), - connector_id + "{}/api/invoices/custom_id/{custom_id}", + self.base_url(connectors) )) } From e5ad9c5c35f386486afedded90c46793196a17d0 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Tue, 3 Oct 2023 19:14:23 +0530 Subject: [PATCH 07/11] refactor(router): add `#[cfg(not(feature = "kms"))]` feature flag to test the simplified apple pay flow locally (#2200) --- crates/router/src/consts.rs | 1 - crates/router/src/core/payments.rs | 2 -- crates/router/src/core/payments/helpers.rs | 23 ++++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index adde413730cb..228d02e1ddac 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -41,7 +41,6 @@ pub(crate) const APPLEPAY_VALIDATION_URL: &str = pub(crate) const QR_IMAGE_DATA_SOURCE_STRING: &str = "data:image/png;base64"; // OID (Object Identifier) for the merchant ID field extension. -#[cfg(feature = "kms")] pub(crate) const MERCHANT_ID_FIELD_EXTENSION_ID: &str = "1.2.840.113635.100.6.32"; pub(crate) const METRICS_HOST_TAG_NAME: &str = "host"; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index cb2b24c4158c..708e20d20504 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -15,7 +15,6 @@ use data_models::mandates::MandateData; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use error_stack::{IntoReport, ResultExt}; use futures::future::join_all; -#[cfg(feature = "kms")] use helpers::ApplePayData; use masking::Secret; use router_env::{instrument, tracing}; @@ -643,7 +642,6 @@ where // Tokenization Action will be DecryptApplePayToken, only when payment method type is Apple Pay // and the connector supports Apple Pay predecrypt - #[cfg(feature = "kms")] if matches!( tokenization_action, TokenizationAction::DecryptApplePayToken diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index d964b4b2e2b2..60ebfd2bdf0f 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -16,26 +16,23 @@ use error_stack::{report, IntoReport, ResultExt}; use external_services::kms; use josekit::jwe; use masking::{ExposeInterface, PeekInterface}; -#[cfg(feature = "kms")] -use openssl::derive::Deriver; -#[cfg(feature = "kms")] -use openssl::pkey::PKey; -#[cfg(feature = "kms")] -use openssl::symm::{decrypt_aead, Cipher}; +use openssl::{ + derive::Deriver, + pkey::PKey, + symm::{decrypt_aead, Cipher}, +}; use router_env::{instrument, logger, tracing}; use time::Duration; use uuid::Uuid; -#[cfg(feature = "kms")] use x509_parser::parse_x509_certificate; use super::{ operations::{BoxedOperation, Operation, PaymentResponse}, CustomerDetails, PaymentData, }; -#[cfg(feature = "kms")] -use crate::connector; use crate::{ configs::settings::{ConnectorRequestReferenceIdConfig, Server, TempLockerDisableConfig}, + connector, consts::{self, BASE64_ENGINE}, core::{ errors::{self, CustomResult, RouterResult, StorageErrorExt}, @@ -3213,7 +3210,6 @@ pub struct ApplePayHeader { transaction_id: masking::Secret, } -#[cfg(feature = "kms")] impl ApplePayData { pub fn token_json( wallet_data: api_models::payments::WalletData, @@ -3241,12 +3237,16 @@ impl ApplePayData { &self, state: &AppState, ) -> CustomResult { + #[cfg(feature = "kms")] let cert_data = kms::get_kms_client(&state.conf.kms) .await .decrypt(&state.conf.applepay_decrypt_keys.apple_pay_ppc) .await .change_context(errors::ApplePayDecryptionError::DecryptionFailed)?; + #[cfg(not(feature = "kms"))] + let cert_data = &state.conf.applepay_decrypt_keys.apple_pay_ppc; + let base64_decode_cert_data = BASE64_ENGINE .decode(cert_data) .into_report() @@ -3297,12 +3297,15 @@ impl ApplePayData { .change_context(errors::ApplePayDecryptionError::KeyDeserializationFailed) .attach_printable("Failed to deserialize the public key")?; + #[cfg(feature = "kms")] let decrypted_apple_pay_ppc_key = kms::get_kms_client(&state.conf.kms) .await .decrypt(&state.conf.applepay_decrypt_keys.apple_pay_ppc_key) .await .change_context(errors::ApplePayDecryptionError::DecryptionFailed)?; + #[cfg(not(feature = "kms"))] + let decrypted_apple_pay_ppc_key = &state.conf.applepay_decrypt_keys.apple_pay_ppc_key; // Create PKey objects from EcKey let private_key = PKey::private_key_from_pem(decrypted_apple_pay_ppc_key.as_bytes()) .into_report() From 0aa6b30d2c9056e9a21a88bdc064daa7e8659bd6 Mon Sep 17 00:00:00 2001 From: SamraatBansal <55536657+SamraatBansal@users.noreply.github.com> Date: Tue, 3 Oct 2023 19:57:42 +0530 Subject: [PATCH 08/11] fix(connector): [noon] add connector_auth params and update description (#2429) --- config/config.example.toml | 1 + config/development.toml | 1 + config/docker_compose.toml | 1 + crates/router/src/configs/settings.rs | 11 +++- crates/router/src/connector/noon.rs | 57 +++++++++++-------- .../router/src/connector/noon/transformers.rs | 11 +++- loadtest/config/development.toml | 1 + 7 files changed, 57 insertions(+), 26 deletions(-) diff --git a/config/config.example.toml b/config/config.example.toml index 0841b26a37ca..d4b44a062a08 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -187,6 +187,7 @@ multisafepay.base_url = "https://testapi.multisafepay.com/" nexinets.base_url = "https://apitest.payengine.de/v1" nmi.base_url = "https://secure.nmi.com/" noon.base_url = "https://api-test.noonpayments.com/" +noon.key_mode = "Test" nuvei.base_url = "https://ppp-test.nuvei.com/" opayo.base_url = "https://pi-test.sagepay.com/" opennode.base_url = "https://dev-api.opennode.com" diff --git a/config/development.toml b/config/development.toml index 0f95dcb6577d..26a7251dc9fb 100644 --- a/config/development.toml +++ b/config/development.toml @@ -161,6 +161,7 @@ multisafepay.base_url = "https://testapi.multisafepay.com/" nexinets.base_url = "https://apitest.payengine.de/v1" nmi.base_url = "https://secure.nmi.com/" noon.base_url = "https://api-test.noonpayments.com/" +noon.key_mode = "Test" nuvei.base_url = "https://ppp-test.nuvei.com/" opayo.base_url = "https://pi-test.sagepay.com/" opennode.base_url = "https://dev-api.opennode.com" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 2ff1c6ab3364..ca6b9eeda5b0 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -105,6 +105,7 @@ multisafepay.base_url = "https://testapi.multisafepay.com/" nexinets.base_url = "https://apitest.payengine.de/v1" nmi.base_url = "https://secure.nmi.com/" noon.base_url = "https://api-test.noonpayments.com/" +noon.key_mode = "Test" nuvei.base_url = "https://ppp-test.nuvei.com/" opayo.base_url = "https://pi-test.sagepay.com/" opennode.base_url = "https://dev-api.opennode.com" diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index b5e522cf843a..fd4a398fd568 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -523,7 +523,7 @@ pub struct Connectors { pub multisafepay: ConnectorParams, pub nexinets: ConnectorParams, pub nmi: ConnectorParams, - pub noon: ConnectorParams, + pub noon: ConnectorParamsWithModeType, pub nuvei: ConnectorParams, pub opayo: ConnectorParams, pub opennode: ConnectorParams, @@ -552,6 +552,15 @@ pub struct ConnectorParams { pub secondary_base_url: Option, } +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithModeType { + pub base_url: String, + pub secondary_base_url: Option, + /// Can take values like Test or Live for Noon + pub key_mode: String, +} + #[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] #[serde(default)] pub struct ConnectorParamsWithMoreUrls { diff --git a/crates/router/src/connector/noon.rs b/crates/router/src/connector/noon.rs index deda24bfbd66..8b7c984083c7 100644 --- a/crates/router/src/connector/noon.rs +++ b/crates/router/src/connector/noon.rs @@ -65,7 +65,7 @@ where fn build_headers( &self, req: &types::RouterData, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { let mut header = vec![( headers::CONTENT_TYPE.to_string(), @@ -73,12 +73,43 @@ where .to_string() .into(), )]; - let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + let mut api_key = get_auth_header(&req.connector_auth_type, connectors, req.test_mode)?; header.append(&mut api_key); Ok(header) } } +fn get_auth_header( + auth_type: &types::ConnectorAuthType, + connectors: &settings::Connectors, + test_mode: Option, +) -> CustomResult)>, errors::ConnectorError> { + let auth = noon::NoonAuthType::try_from(auth_type)?; + + let encoded_api_key = auth + .business_identifier + .zip(auth.application_identifier) + .zip(auth.api_key) + .map(|((business_identifier, application_identifier), api_key)| { + consts::BASE64_ENGINE.encode(format!( + "{}.{}:{}", + business_identifier, application_identifier, api_key + )) + }); + let key_mode = test_mode.map_or(connectors.noon.key_mode.clone(), |is_test_mode| { + if is_test_mode { + "Test".to_string() + } else { + "Live".to_string() + } + }); + + Ok(vec![( + headers::AUTHORIZATION.to_string(), + format!("Key_{} {}", key_mode, encoded_api_key.peek()).into_masked(), + )]) +} + impl ConnectorCommon for Noon { fn id(&self) -> &'static str { "noon" @@ -92,28 +123,6 @@ impl ConnectorCommon for Noon { connectors.noon.base_url.as_ref() } - fn get_auth_header( - &self, - auth_type: &types::ConnectorAuthType, - ) -> CustomResult)>, errors::ConnectorError> { - let auth = noon::NoonAuthType::try_from(auth_type)?; - - let encoded_api_key = auth - .business_identifier - .zip(auth.application_identifier) - .zip(auth.api_key) - .map(|((business_identifier, application_identifier), api_key)| { - consts::BASE64_ENGINE.encode(format!( - "{}.{}:{}", - business_identifier, application_identifier, api_key - )) - }); - Ok(vec![( - headers::AUTHORIZATION.to_string(), - format!("Key_Test {}", encoded_api_key.peek()).into_masked(), - )]) - } - fn build_error_response( &self, res: Response, diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index eff626189103..d7dc832b445d 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -238,7 +238,16 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest { item.request.order_category.clone(), ), }; - let name = item.get_description()?; + + // The description should not have leading or trailing whitespaces, also it should not have double whitespaces and a max 50 chars according to Noon's Docs + let name: String = item + .get_description()? + .trim() + .replace(" ", " ") + .chars() + .take(50) + .collect(); + let (subscription, tokenize_c_c) = match item.request.setup_future_usage.is_some().then_some(( NoonSubscriptionData { diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index ebab00cac144..1b413b1cdf8c 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -91,6 +91,7 @@ multisafepay.base_url = "https://testapi.multisafepay.com/" nexinets.base_url = "https://apitest.payengine.de/v1" nmi.base_url = "https://secure.nmi.com/" noon.base_url = "https://api-test.noonpayments.com/" +noon.key_mode = "Test" nuvei.base_url = "https://ppp-test.nuvei.com/" opayo.base_url = "https://pi-test.sagepay.com/" opennode.base_url = "https://dev-api.opennode.com" From d81762a8b430ca1f197d7dabb26167f54e235735 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa <70575890+prasunna09@users.noreply.github.com> Date: Tue, 3 Oct 2023 20:09:34 +0530 Subject: [PATCH 09/11] feat(pm_list): [Trustpay] add bank_redirect - blik pm type required field info for trustpay (#2390) --- crates/api_models/src/payments.rs | 2 +- crates/router/src/configs/defaults.rs | 79 +++++++++++++++++++ .../src/connector/adyen/transformers.rs | 6 +- .../src/connector/stripe/transformers.rs | 8 +- openapi/openapi_spec.json | 6 +- 5 files changed, 93 insertions(+), 8 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 6711f202e976..31fa00cef9ae 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -868,7 +868,7 @@ pub enum BankRedirectData { Bizum {}, Blik { // Blik Code - blik_code: String, + blik_code: Option, }, Eps { /// The billing details for bank redirection diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index cebb0014064e..ec06a6d7078f 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -3840,6 +3840,85 @@ impl Default for super::settings::RequiredFields { ]), }, ), + ( + enums::PaymentMethodType::Blik, + ConnectorFields { + fields: HashMap::from([ + ( + enums::Connector::Adyen, + RequiredFieldFinal { + mandate: HashMap::new(), + non_mandate: HashMap::new(), + common: HashMap::from([ + ( + "payment_method_data.bank_redirect.blik.blik_code".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.bank_redirect.blik.blik_code".to_string(), + display_name: "blik_code".to_string(), + field_type: enums::FieldType::UserBlikCode, + value: None, + } + ) + ]), + } + ), + ( + enums::Connector::Stripe, + RequiredFieldFinal { + mandate: HashMap::new(), + non_mandate: HashMap::new(), + common: HashMap::from([ + ( + "payment_method_data.bank_redirect.blik.blik_code".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.bank_redirect.blik.blik_code".to_string(), + display_name: "blik_code".to_string(), + field_type: enums::FieldType::UserBlikCode, + value: None, + } + ) + ]), + } + ), + ( + enums::Connector::Trustpay, + RequiredFieldFinal { + mandate: HashMap::new(), + non_mandate: HashMap::new(), + common: HashMap::from([ + ( + "billing.address.first_name".to_string(), + RequiredFieldInfo { + required_field: "billing.address.first_name".to_string(), + display_name: "billing_first_name".to_string(), + field_type: enums::FieldType::UserBillingName, + value: None, + } + ), + ( + "billing.address.last_name".to_string(), + RequiredFieldInfo { + required_field: "billing.address.last_name".to_string(), + display_name: "billing_last_name".to_string(), + field_type: enums::FieldType::UserBillingName, + value: None, + } + ), + ( + "email".to_string(), + RequiredFieldInfo { + required_field: "email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + } + ), + ]), + } + ) + ]), + }, + ), ])), ), ( diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 708d795db0e1..94c7f0c58778 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2034,7 +2034,11 @@ impl<'a> TryFrom<&api_models::payments::BankRedirectData> for AdyenPaymentMethod api_models::payments::BankRedirectData::Blik { blik_code } => { Ok(AdyenPaymentMethod::Blik(Box::new(BlikRedirectionData { payment_type: PaymentType::Blik, - blik_code: blik_code.to_string(), + blik_code: blik_code.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "blik_code", + }, + )?, }))) } api_models::payments::BankRedirectData::Eps { bank_name, .. } => Ok( diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index ce434aa8ba1d..488a7a295bd2 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1582,7 +1582,7 @@ impl TryFrom<&payments::BankRedirectData> for StripePaymentMethodData { type Error = error_stack::Report; fn try_from(bank_redirect_data: &payments::BankRedirectData) -> Result { let payment_method_data_type = StripePaymentMethodType::try_from(bank_redirect_data)?; - match bank_redirect_data.deref() { + match bank_redirect_data { payments::BankRedirectData::BancontactCard { .. } => Ok(Self::BankRedirect( StripeBankRedirectData::StripeBancontactCard(Box::new(StripeBancontactCard { payment_method_data_type, @@ -1591,7 +1591,11 @@ impl TryFrom<&payments::BankRedirectData> for StripePaymentMethodData { payments::BankRedirectData::Blik { blik_code } => Ok(Self::BankRedirect( StripeBankRedirectData::StripeBlik(Box::new(StripeBlik { payment_method_data_type, - code: blik_code.to_owned(), + code: blik_code.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "blik_code", + }, + )?, })), )), payments::BankRedirectData::Eps { bank_name, .. } => Ok(Self::BankRedirect( diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 1d943117c8e9..efa7ebb32cfe 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -2911,12 +2911,10 @@ "properties": { "blik": { "type": "object", - "required": [ - "blik_code" - ], "properties": { "blik_code": { - "type": "string" + "type": "string", + "nullable": true } } } From 34099baa2ec2f73598c4433b0a481dec3fde8c05 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:57:51 +0000 Subject: [PATCH 10/11] test(postman): update postman collection files --- .../bluesnap.postman_collection.json | 1684 ++++++++--------- 1 file changed, 842 insertions(+), 842 deletions(-) diff --git a/postman/collection-json/bluesnap.postman_collection.json b/postman/collection-json/bluesnap.postman_collection.json index d2073774a703..7e12aa04849b 100644 --- a/postman/collection-json/bluesnap.postman_collection.json +++ b/postman/collection-json/bluesnap.postman_collection.json @@ -839,7 +839,7 @@ "name": "Happy Cases", "item": [ { - "name": "Scenario1-Create payment with confirm true", + "name": "Scenario8-Refund full payment", "item": [ { "name": "Payments - Create", @@ -1077,87 +1077,61 @@ "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": "Scenario2-Create payment with confirm false", - "item": [ + }, { - "name": "Payments - Create", + "name": "Refunds - Create", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + "pm.test(\"[POST]::/refunds - 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.test(\"[POST]::/refunds - 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);", + "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", + "if (jsonData?.refund_id) {", + " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " \"- use {{refund_id}} as collection variable for value\",", + " jsonData.refund_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_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]::/refunds - Content check if value for 'status' matches 'succeeded'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " },", " );", "}", "", - "// Response body should have value \"requires_confirmation\" for \"status\"", + "// Response body should have value \"6540\" for \"amount\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_confirmation'\",", + " \"[POST]::/refunds - Content check if value for 'amount' matches '6540'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_confirmation\");", + " pm.expect(jsonData.amount).to.eql(6540);", " },", " );", "}", @@ -1186,45 +1160,139 @@ "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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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": "{\"payment_id\":\"{{payment_id}}\",\"amount\":6540,\"reason\":\"Customer returned product\",\"refund_type\":\"instant\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { - "raw": "{{baseUrl}}/payments", + "raw": "{{baseUrl}}/refunds", "host": [ "{{baseUrl}}" ], "path": [ - "payments" + "refunds" ] }, - "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 create a refund against an already processed payment" }, "response": [] }, { - "name": "Payments - Confirm", + "name": "Refunds - Retrieve", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments/:id/confirm - Status code is 2xx\", function () {", + "pm.test(\"[GET]::/refunds/:id - 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\",", - " );", - " },", - ");", + "pm.test(\"[GET]::/refunds/: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) {}", + "", + "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", + "if (jsonData?.refund_id) {", + " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + " console.log(", + " \"- use {{refund_id}} as collection variable for value\",", + " jsonData.refund_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/refunds - Content check if value for 'status' matches 'succeeded'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " },", + " );", + "}", + "", + "// Response body should have value \"6540\" for \"amount\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/refunds - Content check if value for 'amount' matches '6540'\",", + " function () {", + " pm.expect(jsonData.amount).to.eql(6540);", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/refunds/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "refunds", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{refund_id}}", + "description": "(Required) unique refund id" + } + ] + }, + "description": "To retrieve the properties of a Refund. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, + { + "name": "Scenario9-Partial refund", + "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/:id/confirm - Response has JSON Body\", function () {", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", " pm.response.to.have.jsonBody();", "});", "", @@ -1276,7 +1344,7 @@ "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", @@ -1289,26 +1357,6 @@ } ], "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": [ { @@ -1327,27 +1375,18 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\"}" + "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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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/:id/confirm", + "raw": "{{baseUrl}}/payments", "host": [ "{{baseUrl}}" ], "path": [ - "payments", - ":id", - "confirm" - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } + "payments" ] }, - "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" + "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": [] }, @@ -1420,10 +1459,10 @@ " );", "}", "", - "// Response body should have value \"succeeded\" 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 'succeeded'\",", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", @@ -1469,208 +1508,61 @@ "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": "Scenario3-Create payment without PMD", - "item": [ + }, { - "name": "Payments - Create", + "name": "Refunds - Create", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + "pm.test(\"[POST]::/refunds - 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.test(\"[POST]::/refunds - 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);", + "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", + "if (jsonData?.refund_id) {", + " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " \"- use {{refund_id}} as collection variable for value\",", + " jsonData.refund_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", " );", "}", "", - "// Response body should have value \"requires_payment_method\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'\",", + " \"[POST]::/refunds - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_payment_method\");", + " 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\":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\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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 - 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 \"succeeded\" for \"status\"", + "// Response body should have value \"540\" for \"amount\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments:id/confirm - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/refunds - Content check if value for 'amount' matches '540'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " pm.expect(jsonData.amount).to.eql(540);", " },", " );", "}", @@ -1681,26 +1573,6 @@ } ], "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": [ { @@ -1719,105 +1591,75 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}}}" + "raw": "{\"payment_id\":\"{{payment_id}}\",\"amount\":540,\"reason\":\"Customer returned product\",\"refund_type\":\"instant\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { - "raw": "{{baseUrl}}/payments/:id/confirm", + "raw": "{{baseUrl}}/refunds", "host": [ "{{baseUrl}}" ], "path": [ - "payments", - ":id", - "confirm" - ], - "variable": [ - { - "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" - } + "refunds" ] }, - "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" + "description": "To create a refund against an already processed payment" }, "response": [] }, { - "name": "Payments - Retrieve", + "name": "Refunds - Retrieve", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "pm.test(\"[GET]::/refunds/: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.test(\"[GET]::/refunds/: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);", + "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", + "if (jsonData?.refund_id) {", + " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " \"- use {{refund_id}} as collection variable for value\",", + " jsonData.refund_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_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]::/refunds - Content check if value for 'status' matches 'succeeded'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " },", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", + "// Response body should have value \"6540\" for \"amount\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/refunds - Content check if value for 'amount' matches '540'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " pm.expect(jsonData.amount).to.eql(540);", " },", " );", "}", @@ -1836,112 +1678,80 @@ } ], "url": { - "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "raw": "{{baseUrl}}/refunds/:id", "host": [ "{{baseUrl}}" ], "path": [ - "payments", + "refunds", ":id" ], - "query": [ - { - "key": "force_sync", - "value": "true" - } - ], "variable": [ { "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" + "value": "{{refund_id}}", + "description": "(Required) unique refund 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" + "description": "To retrieve the properties of a Refund. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" }, "response": [] - } - ] - }, - { - "name": "Scenario4-Create payment with Manual capture", - "item": [ + }, { - "name": "Payments - Create", + "name": "Refunds - Create-copy", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + "pm.test(\"[POST]::/refunds - 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.test(\"[POST]::/refunds - 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);", + "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", + "if (jsonData?.refund_id) {", + " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", " console.log(", - " \"- use {{mandate_id}} as collection variable for value\",", - " jsonData.mandate_id,", + " \"- use {{refund_id}} as collection variable for value\",", + " jsonData.refund_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_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]::/refunds - Content check if value for 'status' matches 'succeeded'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " },", " );", "}", "", - "// Response body should have value \"requires_capture\" for \"status\"", + "// Response body should have value \"1000\" for \"amount\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", + " \"[POST]::/refunds - Content check if value for 'amount' matches '1000'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_capture\");", + " pm.expect(jsonData.amount).to.eql(1000);", " },", " );", "}", @@ -1970,97 +1780,63 @@ "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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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": "{\"payment_id\":\"{{payment_id}}\",\"amount\":1000,\"reason\":\"Customer returned product\",\"refund_type\":\"instant\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { - "raw": "{{baseUrl}}/payments", + "raw": "{{baseUrl}}/refunds", "host": [ "{{baseUrl}}" ], "path": [ - "payments" + "refunds" ] }, - "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 create a refund against an already processed payment" }, "response": [] }, { - "name": "Payments - Capture", + "name": "Refunds - Retrieve-copy", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments/:id/capture - Status code is 2xx\", function () {", + "pm.test(\"[GET]::/refunds/:id - Status code is 2xx\", function () {", " pm.response.to.be.success;", "});", "", "// Validate if response header has matching content-type", - "pm.test(", - " \"[POST]::/payments/:id/capture - 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/capture - 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.test(\"[GET]::/refunds/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", " );", - "}", + "});", "", - "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", - "if (jsonData?.client_secret) {", - " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", + "if (jsonData?.refund_id) {", + " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", " console.log(", - " \"- use {{client_secret}} as collection variable for value\",", - " jsonData.client_secret,", + " \"- use {{refund_id}} as collection variable for value\",", + " jsonData.refund_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", " );", "}", "", "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/refunds - Content check if value for 'status' matches 'succeeded'\",", " function () {", " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", @@ -2068,21 +1844,11 @@ "}", "", "// Response body should have value \"6540\" for \"amount\"", - "if (jsonData?.amount) {", - " pm.test(", - " \"[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\"", - "if (jsonData?.amount_received) {", + "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", + " \"[POST]::/refunds - Content check if value for 'amount' matches '1000'\",", " function () {", - " pm.expect(jsonData.amount_received).to.eql(6000);", + " pm.expect(jsonData.amount).to.eql(1000);", " },", " );", "}", @@ -2093,50 +1859,36 @@ } ], "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_to_capture\":6000,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" - }, "url": { - "raw": "{{baseUrl}}/payments/:id/capture", + "raw": "{{baseUrl}}/refunds/:id", "host": [ "{{baseUrl}}" ], "path": [ - "payments", - ":id", - "capture" + "refunds", + ":id" ], "variable": [ { "key": "id", - "value": "{{payment_id}}", - "description": "(Required) unique payment id" + "value": "{{refund_id}}", + "description": "(Required) unique refund id" } ] }, - "description": "To capture the funds for an uncaptured payment" + "description": "To retrieve the properties of a Refund. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" }, "response": [] }, { - "name": "Payments - Retrieve", + "name": "Payments - Retrieve-copy", "event": [ { "listen": "test", @@ -2204,15 +1956,20 @@ " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", + "// Response body should have value \"Succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", + "", + "// Response body should have \"refunds\"", + "pm.test(\"[POST]::/payments - Content check if 'refunds' exists\", function () {", + " pm.expect(typeof jsonData.refunds !== \"undefined\").to.be.true;", + "});", "" ], "type": "text/javascript" @@ -2257,7 +2014,7 @@ ] }, { - "name": "Scenario5-Void the payment", + "name": "Scenario1-Create payment with confirm true", "item": [ { "name": "Payments - Create", @@ -2328,12 +2085,12 @@ " );", "}", "", - "// Response body should have value \"requires_capture\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_capture\");", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", @@ -2362,7 +2119,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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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", @@ -2378,29 +2135,26 @@ "response": [] }, { - "name": "Payments - Cancel", + "name": "Payments - Retrieve", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments/:id/cancel - Status code is 2xx\", function () {", + "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/:id/cancel - Content-Type is application/json\",", - " function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - " },", - ");", + "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(\"[POST]::/payments/:id/cancel - Response has JSON Body\", function () {", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", " pm.response.to.have.jsonBody();", "});", "", @@ -2423,6 +2177,19 @@ " );", "}", "", + "// 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);", @@ -2436,12 +2203,12 @@ " );", "}", "", - "// Response body should have value \"cancelled\" for \"status\"", + "// Response body should have value \"Succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments/:id/cancel - Content check if value for 'status' matches 'cancelled'\",", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"cancelled\");", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", @@ -2452,35 +2219,27 @@ } ], "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": "{\"cancellation_reason\":\"requested_by_customer\"}" - }, "url": { - "raw": "{{baseUrl}}/payments/:id/cancel", + "raw": "{{baseUrl}}/payments/:id?force_sync=true", "host": [ "{{baseUrl}}" ], "path": [ "payments", - ":id", - "cancel" + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } ], "variable": [ { @@ -2490,31 +2249,36 @@ } ] }, - "description": "A Payment could can be cancelled when it is in one of these statuses: requires_payment_method, requires_capture, requires_confirmation, requires_customer_action" + "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": "Scenario2-Create payment with confirm false", + "item": [ { - "name": "Payments - Retrieve", + "name": "Payments - Create", "event": [ { "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\",", " );", "});", "", "// Validate if response has JSON Body", - "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", " pm.response.to.have.jsonBody();", "});", "", @@ -2563,12 +2327,12 @@ " );", "}", "", - "// Response body should have value \"cancelled\" for \"status\"", + "// Response body should have value \"requires_confirmation\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments/:id - Content check if value for 'status' matches 'cancelled'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_confirmation'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"cancelled\");", + " pm.expect(jsonData.status).to.eql(\"requires_confirmation\");", " },", " );", "}", @@ -2579,66 +2343,63 @@ } ], "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\":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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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/:id?force_sync=true", + "raw": "{{baseUrl}}/payments", "host": [ "{{baseUrl}}" ], "path": [ - "payments", - ":id" - ], - "query": [ - { - "key": "force_sync", - "value": "true" - } - ], - "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": "Scenario6-Create 3DS payment", - "item": [ + }, { - "name": "Payments - Create", + "name": "Payments - Confirm", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + "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 - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", - "});", + "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 - Response has JSON Body\", function () {", + "pm.test(\"[POST]::/payments/:id/confirm - Response has JSON Body\", function () {", " pm.response.to.have.jsonBody();", "});", "", @@ -2687,24 +2448,15 @@ " );", "}", "", - "// Response body should have value \"requires_customer_action\" for \"status\"", + "// Response body should have value \"succeeded\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_customer_action'\",", + " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", - "", - "// 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;", - " },", - ");", "" ], "type": "text/javascript" @@ -2712,6 +2464,26 @@ } ], "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": [ { @@ -2730,18 +2502,27 @@ "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\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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": "{\"client_secret\":\"{{client_secret}}\"}" }, "url": { - "raw": "{{baseUrl}}/payments", + "raw": "{{baseUrl}}/payments/:id/confirm", "host": [ "{{baseUrl}}" ], "path": [ - "payments" + "payments", + ":id", + "confirm" + ], + "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": "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": [] }, @@ -2814,12 +2595,12 @@ " );", "}", "", - "// Response body should have value \"requires_customer_action\" 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 'requires_customer_action'\",", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", @@ -2867,7 +2648,7 @@ ] }, { - "name": "Scenario7-Create 3DS payment with confrm false", + "name": "Scenario3-Create payment without PMD", "item": [ { "name": "Payments - Create", @@ -2938,12 +2719,12 @@ " );", "}", "", - "// Response body should have value \"requires_confirmation\" for \"status\"", + "// 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_confirmation'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_confirmation\");", + " pm.expect(jsonData.status).to.eql(\"requires_payment_method\");", " },", " );", "}", @@ -2972,7 +2753,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\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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", @@ -3059,24 +2840,15 @@ " );", "}", "", - "// Response body should have value \"requires_customer_action\" 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 'requires_customer_action'\",", + " \"[POST]::/payments:id/confirm - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", - "", - "// Response body should have \"next_action.redirect_to_url\"", - "pm.test(", - " \"[POST]::/payments/:id/confirm - Content check if 'next_action.redirect_to_url' exists\",", - " function () {", - " pm.expect(typeof jsonData.next_action.redirect_to_url !== \"undefined\").to.be", - " .true;", - " },", - ");", "" ], "type": "text/javascript" @@ -3122,7 +2894,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\"}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm", @@ -3215,12 +2987,12 @@ " );", "}", "", - "// Response body should have value \"requires_customer_action\" 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 'requires_customer_action'\",", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", @@ -3268,7 +3040,7 @@ ] }, { - "name": "Scenario9-Refund full payment", + "name": "Scenario4-Create payment with Manual capture", "item": [ { "name": "Payments - Create", @@ -3339,12 +3111,12 @@ " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", + "// Response body should have value \"requires_capture\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " pm.expect(jsonData.status).to.eql(\"requires_capture\");", " },", " );", "}", @@ -3373,7 +3145,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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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", @@ -3389,26 +3161,29 @@ "response": [] }, { - "name": "Payments - Retrieve", + "name": "Payments - Capture", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "pm.test(\"[POST]::/payments/:id/capture - 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\",", - " );", - "});", + "pm.test(", + " \"[POST]::/payments/:id/capture - 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.test(\"[POST]::/payments/:id/capture - Response has JSON Body\", function () {", " pm.response.to.have.jsonBody();", "});", "", @@ -3457,15 +3232,35 @@ " );", "}", "", - "// Response body should have value \"Succeeded\" 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 'succeeded'\",", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'succeeded'\",", " function () {", " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", + "", + "// Response body should have value \"6540\" for \"amount\"", + "if (jsonData?.amount) {", + " pm.test(", + " \"[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\"", + "if (jsonData?.amount_received) {", + " pm.test(", + " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", + " function () {", + " pm.expect(jsonData.amount_received).to.eql(6000);", + " },", + " );", + "}", "" ], "type": "text/javascript" @@ -3473,27 +3268,35 @@ } ], "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_to_capture\":6000,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" + }, "url": { - "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "raw": "{{baseUrl}}/payments/:id/capture", "host": [ "{{baseUrl}}" ], "path": [ "payments", - ":id" - ], - "query": [ - { - "key": "force_sync", - "value": "true" - } + ":id", + "capture" ], "variable": [ { @@ -3503,64 +3306,85 @@ } ] }, - "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 capture the funds for an uncaptured payment" }, "response": [] }, { - "name": "Refunds - Create", + "name": "Payments - Retrieve", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/refunds - Status code is 2xx\", function () {", + "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]::/refunds - Content-Type is application/json\", function () {", + "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 refund_id as variable for jsonData.payment_id", - "if (jsonData?.refund_id) {", - " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + "// 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 {{refund_id}} as collection variable for value\",", - " jsonData.refund_id,", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/refunds - Content check if value for 'status' matches 'pending'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"pending\");", - " },", + "// 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.\",", " );", "}", "", - "// Response body should have value \"6540\" for \"amount\"", + "// 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]::/refunds - Content check if value for 'amount' matches '6540'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", " function () {", - " pm.expect(jsonData.amount).to.eql(6540);", + " pm.expect(jsonData.status).to.eql(\"succeeded\");", " },", " );", "}", @@ -3571,93 +3395,120 @@ } ], "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": "{\"payment_id\":\"{{payment_id}}\",\"amount\":6540,\"reason\":\"Customer returned product\",\"refund_type\":\"instant\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" - }, "url": { - "raw": "{{baseUrl}}/refunds", + "raw": "{{baseUrl}}/payments/:id?force_sync=true", "host": [ "{{baseUrl}}" ], "path": [ - "refunds" + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } ] }, - "description": "To create a refund against an already processed payment" + "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": "Scenario5-Void the payment", + "item": [ { - "name": "Refunds - Retrieve", + "name": "Payments - Create", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[GET]::/refunds/: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]::/refunds/: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\",", " );", "});", "", + "// 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 refund_id as variable for jsonData.payment_id", - "if (jsonData?.refund_id) {", - " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + "// 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 {{refund_id}} as collection variable for value\",", - " jsonData.refund_id,", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/refunds - Content check if value for 'status' matches 'succeeded'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", - " },", + "// 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.\",", " );", "}", "", - "// Response body should have value \"6540\" for \"amount\"", + "// 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_capture\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/refunds - Content check if value for 'amount' matches '6540'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", " function () {", - " pm.expect(jsonData.amount).to.eql(6540);", + " pm.expect(jsonData.status).to.eql(\"requires_capture\");", " },", " );", "}", @@ -3668,60 +3519,63 @@ } ], "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\":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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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}}/refunds/:id", + "raw": "{{baseUrl}}/payments", "host": [ "{{baseUrl}}" ], "path": [ - "refunds", - ":id" - ], - "variable": [ - { - "key": "id", - "value": "{{refund_id}}", - "description": "(Required) unique refund id" - } + "payments" ] }, - "description": "To retrieve the properties of a Refund. 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": "Scenario10-Partial refund", - "item": [ + }, { - "name": "Payments - Create", + "name": "Payments - Cancel", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + "pm.test(\"[POST]::/payments/:id/cancel - 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\",", - " );", - "});", + "pm.test(", + " \"[POST]::/payments/:id/cancel - 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.test(\"[POST]::/payments/:id/cancel - Response has JSON Body\", function () {", " pm.response.to.have.jsonBody();", "});", "", @@ -3744,19 +3598,6 @@ " );", "}", "", - "// 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);", @@ -3770,12 +3611,12 @@ " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", + "// Response body should have value \"cancelled\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/payments/:id/cancel - Content check if value for 'status' matches 'cancelled'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " pm.expect(jsonData.status).to.eql(\"cancelled\");", " },", " );", "}", @@ -3804,18 +3645,27 @@ "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\":\"4242424242424242\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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": "{\"cancellation_reason\":\"requested_by_customer\"}" }, "url": { - "raw": "{{baseUrl}}/payments", + "raw": "{{baseUrl}}/payments/:id/cancel", "host": [ "{{baseUrl}}" ], "path": [ - "payments" + "payments", + ":id", + "cancel" + ], + "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": "A Payment could can be cancelled when it is in one of these statuses: requires_payment_method, requires_capture, requires_confirmation, requires_customer_action" }, "response": [] }, @@ -3888,12 +3738,12 @@ " );", "}", "", - "// Response body should have value \"Succeeded\" for \"status\"", + "// Response body should have value \"cancelled\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'\",", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'cancelled'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " pm.expect(jsonData.status).to.eql(\"cancelled\");", " },", " );", "}", @@ -3937,64 +3787,99 @@ "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": "Scenario6-Create 3DS payment", + "item": [ { - "name": "Refunds - Create", + "name": "Payments - Create", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/refunds - 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(\"[POST]::/refunds - 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\",", " );", "});", "", + "// 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 refund_id as variable for jsonData.payment_id", - "if (jsonData?.refund_id) {", - " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + "// 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 {{refund_id}} as collection variable for value\",", - " jsonData.refund_id,", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/refunds - Content check if value for 'status' matches 'pending'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"pending\");", - " },", + "// 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.\",", " );", "}", "", - "// Response body should have value \"540\" for \"amount\"", + "// 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]::/refunds - Content check if value for 'amount' matches '540'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_customer_action'\",", " function () {", - " pm.expect(jsonData.amount).to.eql(540);", + " 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;", + " },", + ");", "" ], "type": "text/javascript" @@ -4020,75 +3905,96 @@ "language": "json" } }, - "raw": "{\"payment_id\":\"{{payment_id}}\",\"amount\":540,\"reason\":\"Customer returned product\",\"refund_type\":\"instant\",\"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\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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}}/refunds", + "raw": "{{baseUrl}}/payments", "host": [ "{{baseUrl}}" ], "path": [ - "refunds" + "payments" ] }, - "description": "To create a refund against an already processed 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": "Refunds - Retrieve", + "name": "Payments - Retrieve", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[GET]::/refunds/:id - Status code is 2xx\", function () {", + "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]::/refunds/:id - Content-Type is application/json\", function () {", + "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 refund_id as variable for jsonData.payment_id", - "if (jsonData?.refund_id) {", - " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + "// 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 {{refund_id}} as collection variable for value\",", - " jsonData.refund_id,", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/refunds - Content check if value for 'status' matches 'succeeded'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", - " },", + "// 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 \"6540\" for \"amount\"", + "// Response body should have value \"requires_customer_action\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/refunds - Content check if value for 'amount' matches '540'\",", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'requires_customer_action'\",", " function () {", - " pm.expect(jsonData.amount).to.eql(540);", + " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", " },", " );", "}", @@ -4107,80 +4013,112 @@ } ], "url": { - "raw": "{{baseUrl}}/refunds/:id", + "raw": "{{baseUrl}}/payments/:id?force_sync=true", "host": [ "{{baseUrl}}" ], "path": [ - "refunds", + "payments", ":id" ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], "variable": [ { "key": "id", - "value": "{{refund_id}}", - "description": "(Required) unique refund id" + "value": "{{payment_id}}", + "description": "(Required) unique payment id" } ] }, - "description": "To retrieve the properties of a Refund. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + "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": "Scenario7-Create 3DS payment with confrm false", + "item": [ { - "name": "Refunds - Create-copy", + "name": "Payments - Create", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[POST]::/refunds - 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(\"[POST]::/refunds - 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\",", " );", "});", "", + "// 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 refund_id as variable for jsonData.payment_id", - "if (jsonData?.refund_id) {", - " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + "// 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 {{refund_id}} as collection variable for value\",", - " jsonData.refund_id,", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/refunds - Content check if value for 'status' matches 'pending'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"pending\");", - " },", + "// 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.\",", " );", "}", "", - "// Response body should have value \"1000\" for \"amount\"", + "// 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_confirmation\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/refunds - Content check if value for 'amount' matches '1000'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_confirmation'\",", " function () {", - " pm.expect(jsonData.amount).to.eql(1000);", + " pm.expect(jsonData.status).to.eql(\"requires_confirmation\");", " },", " );", "}", @@ -4209,38 +4147,46 @@ "language": "json" } }, - "raw": "{\"payment_id\":\"{{payment_id}}\",\"amount\":1000,\"reason\":\"Customer returned product\",\"refund_type\":\"instant\",\"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\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"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\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"abc\"}},\"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}}/refunds", + "raw": "{{baseUrl}}/payments", "host": [ "{{baseUrl}}" ], "path": [ - "refunds" + "payments" ] }, - "description": "To create a refund against an already processed 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": "Refunds - Retrieve-copy", + "name": "Payments - Confirm", "event": [ { "listen": "test", "script": { "exec": [ "// Validate status 2xx", - "pm.test(\"[GET]::/refunds/:id - Status code is 2xx\", function () {", + "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(\"[GET]::/refunds/:id - Content-Type is application/json\", function () {", - " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", - " \"application/json\",", - " );", + "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", @@ -4249,38 +4195,63 @@ " jsonData = pm.response.json();", "} catch (e) {}", "", - "// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id", - "if (jsonData?.refund_id) {", - " pm.collectionVariables.set(\"refund_id\", jsonData.refund_id);", + "// 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 {{refund_id}} as collection variable for value\",", - " jsonData.refund_id,", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", " );", "} else {", " console.log(", - " \"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.\",", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", " );", "}", "", - "// Response body should have value \"succeeded\" for \"status\"", - "if (jsonData?.status) {", - " pm.test(", - " \"[POST]::/refunds - Content check if value for 'status' matches 'succeeded'\",", - " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", - " },", + "// 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.\",", " );", "}", "", - "// Response body should have value \"6540\" for \"amount\"", + "// 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]::/refunds - Content check if value for 'amount' matches '1000'\",", + " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'requires_customer_action'\",", " function () {", - " pm.expect(jsonData.amount).to.eql(1000);", + " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", " },", " );", "}", + "", + "// Response body should have \"next_action.redirect_to_url\"", + "pm.test(", + " \"[POST]::/payments/:id/confirm - Content check if 'next_action.redirect_to_url' exists\",", + " function () {", + " pm.expect(typeof jsonData.next_action.redirect_to_url !== \"undefined\").to.be", + " .true;", + " },", + ");", "" ], "type": "text/javascript" @@ -4288,36 +4259,70 @@ } ], "request": { - "method": "GET", + "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}}\"}" + }, "url": { - "raw": "{{baseUrl}}/refunds/:id", + "raw": "{{baseUrl}}/payments/:id/confirm", "host": [ "{{baseUrl}}" ], "path": [ - "refunds", - ":id" + "payments", + ":id", + "confirm" ], "variable": [ { "key": "id", - "value": "{{refund_id}}", - "description": "(Required) unique refund id" + "value": "{{payment_id}}", + "description": "(Required) unique payment id" } ] }, - "description": "To retrieve the properties of a Refund. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + "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-copy", + "name": "Payments - Retrieve", "event": [ { "listen": "test", @@ -4385,20 +4390,15 @@ " );", "}", "", - "// Response body should have value \"Succeeded\" for \"status\"", + "// 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 'succeeded'\",", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'requires_customer_action'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"succeeded\");", + " pm.expect(jsonData.status).to.eql(\"requires_customer_action\");", " },", " );", "}", - "", - "// Response body should have \"refunds\"", - "pm.test(\"[POST]::/payments - Content check if 'refunds' exists\", function () {", - " pm.expect(typeof jsonData.refunds !== \"undefined\").to.be.true;", - "});", "" ], "type": "text/javascript" From 9a0e637b0fe2cfed70dbe56188a0f3ee082232d4 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:57:51 +0000 Subject: [PATCH 11/11] chore(version): v1.49.0 --- CHANGELOG.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b10db2403b5..c74b50eb0af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,51 @@ All notable changes to HyperSwitch will be documented here. - - - +## 1.49.0 (2023-10-03) + +### Features + +- **connector:** [Nuvei] Add order id as the reference id ([#2408](https://github.com/juspay/hyperswitch/pull/2408)) ([`d5d876b`](https://github.com/juspay/hyperswitch/commit/d5d876b821187648994ea53c358467966e99cd23)) +- **pm_auth:** Added pm_auth_config to merchant_connector_account ([#2183](https://github.com/juspay/hyperswitch/pull/2183)) ([`abfdea2`](https://github.com/juspay/hyperswitch/commit/abfdea20b06a8804ec83fe9431f9a034465bb924)) +- **pm_list:** [Trustpay] add bank_redirect - blik pm type required field info for trustpay ([#2390](https://github.com/juspay/hyperswitch/pull/2390)) ([`d81762a`](https://github.com/juspay/hyperswitch/commit/d81762a8b430ca1f197d7dabb26167f54e235735)) +- **webhooks:** Webhooks effect tracker ([#2260](https://github.com/juspay/hyperswitch/pull/2260)) ([`5048d24`](https://github.com/juspay/hyperswitch/commit/5048d248e59b8ecaf8585ffd5134953cf62e74ef)) + +### Bug Fixes + +- **CI:** Fix spell check for CI pull request ([#2420](https://github.com/juspay/hyperswitch/pull/2420)) ([`3b10b1c`](https://github.com/juspay/hyperswitch/commit/3b10b1c473209e36183271a81eb9014a8f5cddfa)) +- **cards:** Allow card cvc 000 ([#2387](https://github.com/juspay/hyperswitch/pull/2387)) ([`f0dc374`](https://github.com/juspay/hyperswitch/commit/f0dc37438b7a6c4b25acff941aca13545217d307)) +- **configs:** Add `lock_settings` in `docker_compose.toml` ([#2396](https://github.com/juspay/hyperswitch/pull/2396)) ([`14fec5c`](https://github.com/juspay/hyperswitch/commit/14fec5c3980397079fe8861caca589157a8ba242)) +- **connector:** [noon] add connector_auth params and update description ([#2429](https://github.com/juspay/hyperswitch/pull/2429)) ([`0aa6b30`](https://github.com/juspay/hyperswitch/commit/0aa6b30d2c9056e9a21a88bdc064daa7e8659bd6)) +- **payment_methos:** prioritized `apple_pay_combined` deserialization over `apple_pay` ([#2393](https://github.com/juspay/hyperswitch/pull/2393)) ([`f12ce9c`](https://github.com/juspay/hyperswitch/commit/f12ce9c72d94674e0ae0ec7f1c91d8b5c43481e8)) +- Temp support for ach gocardless with existing api contracts ([#2395](https://github.com/juspay/hyperswitch/pull/2395)) ([`d43fbcc`](https://github.com/juspay/hyperswitch/commit/d43fbccd54011d0de6f8d39adbd264d9ada77e7e)) + +### Refactors + +- **connector:** + - [Klarna] Expand wildcard match arms ([#2403](https://github.com/juspay/hyperswitch/pull/2403)) ([`89cb63b`](https://github.com/juspay/hyperswitch/commit/89cb63be3328010d26b5f6322449fc50e80593e4)) + - [Klarna] Enhance currency Mapping with ConnectorCurrencyCommon Trait ([#2414](https://github.com/juspay/hyperswitch/pull/2414)) ([`ee7efd0`](https://github.com/juspay/hyperswitch/commit/ee7efd05adbe14bab1d2862d7ab2bf244c226433)) + - [Cryptopay] Update PSync with connector_request_reference_id ([#2388](https://github.com/juspay/hyperswitch/pull/2388)) ([`3680541`](https://github.com/juspay/hyperswitch/commit/36805411772da00719a716d05c650f10ca990d49)) +- **router:** Add `#[cfg(not(feature = "kms"))]` feature flag to test the simplified apple pay flow locally ([#2200](https://github.com/juspay/hyperswitch/pull/2200)) ([`e5ad9c5`](https://github.com/juspay/hyperswitch/commit/e5ad9c5c35f386486afedded90c46793196a17d0)) + +### Testing + +- **postman:** Update postman collection files ([`34099ba`](https://github.com/juspay/hyperswitch/commit/34099baa2ec2f73598c4433b0a481dec3fde8c05)) + +### Documentation + +- **README:** + - Include Hacktoberfest information ([#2386](https://github.com/juspay/hyperswitch/pull/2386)) ([`e8eb929`](https://github.com/juspay/hyperswitch/commit/e8eb929d5b4d99d09940532e3abbca2b811bcf36)) + - Fixed TOC links ([#2402](https://github.com/juspay/hyperswitch/pull/2402)) ([`c81d8e9`](https://github.com/juspay/hyperswitch/commit/c81d8e9a180da8f71d156d39c9f85847f6d7a572)) + +### Miscellaneous Tasks + +- **deps:** Bump webpki from 0.22.0 to 0.22.2 ([#2419](https://github.com/juspay/hyperswitch/pull/2419)) ([`6bf0e75`](https://github.com/juspay/hyperswitch/commit/6bf0e75b69608ea07fd7601906982a19cdc81400)) + +**Full Changelog:** [`v1.48.1+hotfix.1...v1.49.0`](https://github.com/juspay/hyperswitch/compare/v1.48.1+hotfix.1...v1.49.0) + +- - - + + ## 1.48.1 (2023-09-28) ### Bug Fixes