diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 19ce82e59b..81e50b020e 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -249,6 +249,9 @@ impl Connector { Self::Checkout | Self::Nmi| Self::Cybersource => true, } } + pub fn is_pre_processing_required_before_authorize(&self) -> bool { + matches!(self, Self::Airwallex) + } } #[derive( diff --git a/crates/router/src/connector/airwallex.rs b/crates/router/src/connector/airwallex.rs index ca511534b9..fbc2590948 100644 --- a/crates/router/src/connector/airwallex.rs +++ b/crates/router/src/connector/airwallex.rs @@ -18,7 +18,7 @@ use crate::{ payments, }, events::connector_api_logs::ConnectorEvent, - headers, logger, routes, + headers, logger, services::{ self, request::{self, Mask}, @@ -27,7 +27,6 @@ use crate::{ types::{ self, api::{self, ConnectorCommon, ConnectorCommonExt}, - transformers::ForeignFrom, ErrorResponse, Response, RouterData, }, utils::{crypto, BytesExt}, @@ -123,6 +122,7 @@ impl ConnectorValidation for Airwallex { } impl api::Payment for Airwallex {} +impl api::PaymentsPreProcessing for Airwallex {} impl api::PaymentsCompleteAuthorize for Airwallex {} impl api::MandateSetup for Airwallex {} impl @@ -242,14 +242,14 @@ impl ConnectorIntegration for Airwallex { fn get_headers( &self, - req: &types::PaymentsInitRouterData, + req: &types::PaymentsPreProcessingRouterData, connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) @@ -261,7 +261,7 @@ impl fn get_url( &self, - _req: &types::PaymentsInitRouterData, + _req: &types::PaymentsPreProcessingRouterData, connectors: &settings::Connectors, ) -> CustomResult { Ok(format!( @@ -273,7 +273,7 @@ impl fn get_request_body( &self, - req: &types::PaymentsInitRouterData, + req: &types::PaymentsPreProcessingRouterData, _connectors: &settings::Connectors, ) -> CustomResult { let req_obj = airwallex::AirwallexIntentRequest::try_from(req)?; @@ -282,16 +282,20 @@ impl fn build_request( &self, - req: &types::PaymentsInitRouterData, + req: &types::PaymentsPreProcessingRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Post) - .url(&types::PaymentsInitType::get_url(self, req, connectors)?) + .url(&types::PaymentsPreProcessingType::get_url( + self, req, connectors, + )?) .attach_default_headers() - .headers(types::PaymentsInitType::get_headers(self, req, connectors)?) - .set_body(types::PaymentsInitType::get_request_body( + .headers(types::PaymentsPreProcessingType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsPreProcessingType::get_request_body( self, req, connectors, )?) .build(), @@ -300,10 +304,10 @@ impl fn handle_response( &self, - data: &types::PaymentsInitRouterData, + data: &types::PaymentsPreProcessingRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: airwallex::AirwallexPaymentsResponse = res .response .parse_struct("airwallex AirwallexPaymentsResponse") @@ -335,36 +339,6 @@ impl api::PaymentAuthorize for Airwallex {} impl ConnectorIntegration for Airwallex { - async fn execute_pretasks( - &self, - router_data: &mut types::PaymentsAuthorizeRouterData, - app_state: &routes::AppState, - ) -> CustomResult<(), errors::ConnectorError> { - let integ: Box< - &(dyn ConnectorIntegration< - api::InitPayment, - types::PaymentsAuthorizeData, - types::PaymentsResponseData, - > + Send - + Sync - + 'static), - > = Box::new(&Self); - let authorize_data = &types::PaymentsInitRouterData::foreign_from(( - &router_data.to_owned(), - router_data.request.clone(), - )); - let resp = services::execute_connector_processing_step( - app_state, - integ, - authorize_data, - payments::CallConnectorAction::Trigger, - None, - ) - .await?; - router_data.reference_id = resp.reference_id; - Ok(()) - } - fn get_headers( &self, req: &types::PaymentsAuthorizeRouterData, diff --git a/crates/router/src/connector/airwallex/transformers.rs b/crates/router/src/connector/airwallex/transformers.rs index 587a14caed..56a6ebe2e4 100644 --- a/crates/router/src/connector/airwallex/transformers.rs +++ b/crates/router/src/connector/airwallex/transformers.rs @@ -41,13 +41,26 @@ pub struct AirwallexIntentRequest { //ID created in merchant's order system that corresponds to this PaymentIntent. merchant_order_id: String, } -impl TryFrom<&types::PaymentsInitRouterData> for AirwallexIntentRequest { +impl TryFrom<&types::PaymentsPreProcessingRouterData> for AirwallexIntentRequest { type Error = error_stack::Report; - fn try_from(item: &types::PaymentsInitRouterData) -> Result { + fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result { + // amount and currency will always be Some since PaymentsPreProcessingData is constructed using PaymentsAuthorizeData + let amount = item + .request + .amount + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "amount", + })?; + let currency = + item.request + .currency + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "currency", + })?; Ok(Self { request_id: Uuid::new_v4().to_string(), - amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?, - currency: item.request.currency, + amount: utils::to_currency_base_unit(amount, currency)?, + currency, merchant_order_id: item.connector_request_reference_id.clone(), }) } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index a5f9c2a5e4..3988f3d4fd 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1885,6 +1885,14 @@ where dyn api::Connector: services::api::ConnectorIntegration, { + if !is_operation_complete_authorize(&operation) + && connector + .connector_name + .is_pre_processing_required_before_authorize() + { + router_data = router_data.preprocessing_steps(state, connector).await?; + return Ok((router_data, should_continue_payment)); + } //TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check let router_data_and_should_continue_payment = match payment_data.payment_method_data.clone() { Some(api_models::payments::PaymentMethodData::BankTransfer(data)) => match data.deref() { diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index bf924c2598..158e28e696 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -928,7 +928,6 @@ impl default_imp_for_pre_processing_steps!( connector::Aci, - connector::Airwallex, connector::Authorizedotnet, connector::Bambora, connector::Billwerk, diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 931ea26a9f..5990b29f37 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -322,13 +322,14 @@ pub async fn authorize_preprocessing_steps( ), ], ); - - let authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( + let mut authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( resp.clone(), router_data.request.to_owned(), resp.response, ); - + if connector.connector_name == api_models::enums::Connector::Airwallex { + authorize_router_data.reference_id = resp.reference_id; + } Ok(authorize_router_data) } else { Ok(router_data.clone()) diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/.meta.json new file mode 100644 index 0000000000..e4ef30e39e --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/.meta.json @@ -0,0 +1,7 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Capture", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/.event.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/.event.meta.json new file mode 100644 index 0000000000..0731450e6b --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/.event.meta.json @@ -0,0 +1,3 @@ +{ + "eventOrder": ["event.test.js"] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/event.test.js new file mode 100644 index 0000000000..f560d84ea7 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/event.test.js @@ -0,0 +1,94 @@ +// Validate status 2xx +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( + "[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.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "succeeded" for "status" +if (jsonData?.status) { + pm.test( + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'", + function () { + pm.expect(jsonData.status).to.eql("partially_captured"); + }, + ); +} + +// 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); + }, + ); +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/request.json new file mode 100644 index 0000000000..9fe257ed85 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/request.json @@ -0,0 +1,39 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount_to_capture": 6000, + "statement_descriptor_name": "Joseph", + "statement_descriptor_suffix": "JS" + } + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/capture", + "host": ["{{baseUrl}}"], + "path": ["payments", ":id", "capture"], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To capture the funds for an uncaptured payment" +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/response.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/response.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/.event.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/.event.meta.json new file mode 100644 index 0000000000..0731450e6b --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/.event.meta.json @@ -0,0 +1,3 @@ +{ + "eventOrder": ["event.test.js"] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/event.test.js new file mode 100644 index 0000000000..d683186aa0 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "requires_capture" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'requires_capture'", + function () { + pm.expect(jsonData.status).to.eql("requires_capture"); + }, + ); +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/request.json new file mode 100644 index 0000000000..0cc6a0477a --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/request.json @@ -0,0 +1,84 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": true, + "capture_method": "manual", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 6540, + "customer_id": "AirwallexCustomer", + "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": "4035501000000008", + "card_exp_month": "10", + "card_exp_year": "2025", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "PiX" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "PiX" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": ["{{baseUrl}}"], + "path": ["payments"] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/response.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/response.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/.event.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/.event.meta.json new file mode 100644 index 0000000000..0731450e6b --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/.event.meta.json @@ -0,0 +1,3 @@ +{ + "eventOrder": ["event.test.js"] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/event.test.js new file mode 100644 index 0000000000..ca68dd7045 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[GET]::/payments/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "succeeded" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'partially_captured'", + function () { + pm.expect(jsonData.status).to.eql("partially_captured"); + }, + ); +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/request.json new file mode 100644 index 0000000000..6cd4b7d96c --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/request.json @@ -0,0 +1,28 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": ["{{baseUrl}}"], + "path": ["payments", ":id"], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/response.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/response.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js index 2d7dbc507f..2681a4daa9 100644 --- a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js @@ -88,7 +88,7 @@ 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); + pm.expect(jsonData.amount_received).to.eql(6540); }, ); } diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json index 9fe257ed85..cceb2b55f0 100644 --- a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json @@ -18,7 +18,7 @@ } }, "raw_json_formatted": { - "amount_to_capture": 6000, + "amount_to_capture": 6540, "statement_descriptor_name": "Joseph", "statement_descriptor_suffix": "JS" } diff --git a/postman/collection-json/airwallex.postman_collection.json b/postman/collection-json/airwallex.postman_collection.json index 3e6b467f9b..ce92a22c8b 100644 --- a/postman/collection-json/airwallex.postman_collection.json +++ b/postman/collection-json/airwallex.postman_collection.json @@ -806,6 +806,398 @@ { "name": "Happy Cases", "item": [ + { + "name": "Scenario11-Create payment with Manual Partial capture", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"requires_capture\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"requires_capture\");", + " },", + " );", + "}", + "" + ], + "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\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"AirwallexCustomer\",\"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\":\"4035501000000008\",\"card_exp_month\":\"10\",\"card_exp_year\":\"2025\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + }, + "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 - Capture", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "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(", + " \"[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.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " },", + " );", + "}", + "", + "// 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" + } + } + ], + "request": { + "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/capture", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "capture" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To capture the funds for an uncaptured payment" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'partially_captured'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, { "name": "Scenario1-Create payment with confirm true", "item": [ @@ -2050,7 +2442,7 @@ " pm.test(", " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", - " pm.expect(jsonData.amount_received).to.eql(6000);", + " pm.expect(jsonData.amount_received).to.eql(6540);", " },", " );", "}", @@ -2079,7 +2471,7 @@ "language": "json" } }, - "raw": "{\"amount_to_capture\":6000,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" + "raw": "{\"amount_to_capture\":6540,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" }, "url": { "raw": "{{baseUrl}}/payments/:id/capture",