mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(connector): [Square] Implement Card Payments for Square (#1902)
This commit is contained in:
@ -193,6 +193,7 @@ powertranz.base_url = "https://staging.ptranz.com/api/"
|
|||||||
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
||||||
shift4.base_url = "https://api.shift4.com/"
|
shift4.base_url = "https://api.shift4.com/"
|
||||||
square.base_url = "https://connect.squareupsandbox.com/"
|
square.base_url = "https://connect.squareupsandbox.com/"
|
||||||
|
square.secondary_base_url = "https://pci-connect.squareupsandbox.com/"
|
||||||
stax.base_url = "https://apiprod.fattlabs.com/"
|
stax.base_url = "https://apiprod.fattlabs.com/"
|
||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
stripe.base_url_file_upload = "https://files.stripe.com/"
|
stripe.base_url_file_upload = "https://files.stripe.com/"
|
||||||
@ -306,6 +307,7 @@ stripe = { long_lived_token = false, payment_method = "wallet", payment_method_t
|
|||||||
checkout = { long_lived_token = false, payment_method = "wallet" }
|
checkout = { long_lived_token = false, payment_method = "wallet" }
|
||||||
mollie = {long_lived_token = false, payment_method = "card"}
|
mollie = {long_lived_token = false, payment_method = "card"}
|
||||||
stax = { long_lived_token = true, payment_method = "card,bank_debit" }
|
stax = { long_lived_token = true, payment_method = "card,bank_debit" }
|
||||||
|
square = {long_lived_token = false, payment_method = "card"}
|
||||||
braintree = { long_lived_token = false, payment_method = "card" }
|
braintree = { long_lived_token = false, payment_method = "card" }
|
||||||
|
|
||||||
[dummy_connector]
|
[dummy_connector]
|
||||||
|
|||||||
@ -168,6 +168,7 @@ powertranz.base_url = "https://staging.ptranz.com/api/"
|
|||||||
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
||||||
shift4.base_url = "https://api.shift4.com/"
|
shift4.base_url = "https://api.shift4.com/"
|
||||||
square.base_url = "https://connect.squareupsandbox.com/"
|
square.base_url = "https://connect.squareupsandbox.com/"
|
||||||
|
square.secondary_base_url = "https://pci-connect.squareupsandbox.com/"
|
||||||
stax.base_url = "https://apiprod.fattlabs.com/"
|
stax.base_url = "https://apiprod.fattlabs.com/"
|
||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
stripe.base_url_file_upload = "https://files.stripe.com/"
|
stripe.base_url_file_upload = "https://files.stripe.com/"
|
||||||
@ -366,6 +367,7 @@ stripe = { long_lived_token = false, payment_method = "wallet", payment_method_t
|
|||||||
checkout = { long_lived_token = false, payment_method = "wallet" }
|
checkout = { long_lived_token = false, payment_method = "wallet" }
|
||||||
stax = { long_lived_token = true, payment_method = "card,bank_debit" }
|
stax = { long_lived_token = true, payment_method = "card,bank_debit" }
|
||||||
mollie = {long_lived_token = false, payment_method = "card"}
|
mollie = {long_lived_token = false, payment_method = "card"}
|
||||||
|
square = {long_lived_token = false, payment_method = "card"}
|
||||||
braintree = { long_lived_token = false, payment_method = "card" }
|
braintree = { long_lived_token = false, payment_method = "card" }
|
||||||
|
|
||||||
[connector_customer]
|
[connector_customer]
|
||||||
|
|||||||
@ -114,6 +114,7 @@ powertranz.base_url = "https://staging.ptranz.com/api/"
|
|||||||
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
||||||
shift4.base_url = "https://api.shift4.com/"
|
shift4.base_url = "https://api.shift4.com/"
|
||||||
square.base_url = "https://connect.squareupsandbox.com/"
|
square.base_url = "https://connect.squareupsandbox.com/"
|
||||||
|
square.secondary_base_url = "https://pci-connect.squareupsandbox.com/"
|
||||||
stax.base_url = "https://apiprod.fattlabs.com/"
|
stax.base_url = "https://apiprod.fattlabs.com/"
|
||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
stripe.base_url_file_upload = "https://files.stripe.com/"
|
stripe.base_url_file_upload = "https://files.stripe.com/"
|
||||||
@ -197,6 +198,7 @@ stripe = { long_lived_token = false, payment_method = "wallet", payment_method_t
|
|||||||
checkout = { long_lived_token = false, payment_method = "wallet" }
|
checkout = { long_lived_token = false, payment_method = "wallet" }
|
||||||
mollie = {long_lived_token = false, payment_method = "card"}
|
mollie = {long_lived_token = false, payment_method = "card"}
|
||||||
stax = { long_lived_token = true, payment_method = "card,bank_debit" }
|
stax = { long_lived_token = true, payment_method = "card,bank_debit" }
|
||||||
|
square = {long_lived_token = false, payment_method = "card"}
|
||||||
braintree = { long_lived_token = false, payment_method = "card" }
|
braintree = { long_lived_token = false, payment_method = "card" }
|
||||||
|
|
||||||
[dummy_connector]
|
[dummy_connector]
|
||||||
|
|||||||
@ -108,7 +108,7 @@ pub enum Connector {
|
|||||||
Powertranz,
|
Powertranz,
|
||||||
Rapyd,
|
Rapyd,
|
||||||
Shift4,
|
Shift4,
|
||||||
// Square, added as template code for future usage,
|
Square,
|
||||||
Stax,
|
Stax,
|
||||||
Stripe,
|
Stripe,
|
||||||
Trustpay,
|
Trustpay,
|
||||||
@ -223,7 +223,7 @@ pub enum RoutableConnectors {
|
|||||||
Powertranz,
|
Powertranz,
|
||||||
Rapyd,
|
Rapyd,
|
||||||
Shift4,
|
Shift4,
|
||||||
//Square, added as template code for future usage
|
Square,
|
||||||
Stax,
|
Stax,
|
||||||
Stripe,
|
Stripe,
|
||||||
Trustpay,
|
Trustpay,
|
||||||
|
|||||||
@ -1,14 +1,20 @@
|
|||||||
mod transformers;
|
pub mod transformers;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use api_models::enums;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::ExposeInterface;
|
use masking::PeekInterface;
|
||||||
use transformers as square;
|
use transformers as square;
|
||||||
|
|
||||||
|
use super::utils::RefundsRequestData;
|
||||||
use crate::{
|
use crate::{
|
||||||
configs::settings,
|
configs::settings,
|
||||||
core::errors::{self, CustomResult},
|
consts,
|
||||||
|
core::{
|
||||||
|
errors::{self, CustomResult},
|
||||||
|
payments,
|
||||||
|
},
|
||||||
headers,
|
headers,
|
||||||
services::{
|
services::{
|
||||||
self,
|
self,
|
||||||
@ -39,16 +45,6 @@ impl api::RefundExecute for Square {}
|
|||||||
impl api::RefundSync for Square {}
|
impl api::RefundSync for Square {}
|
||||||
impl api::PaymentToken for Square {}
|
impl api::PaymentToken for Square {}
|
||||||
|
|
||||||
impl
|
|
||||||
ConnectorIntegration<
|
|
||||||
api::PaymentMethodToken,
|
|
||||||
types::PaymentMethodTokenizationData,
|
|
||||||
types::PaymentsResponseData,
|
|
||||||
> for Square
|
|
||||||
{
|
|
||||||
// Not Implemented (R)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Square
|
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Square
|
||||||
where
|
where
|
||||||
Self: ConnectorIntegration<Flow, Request, Response>,
|
Self: ConnectorIntegration<Flow, Request, Response>,
|
||||||
@ -91,7 +87,7 @@ impl ConnectorCommon for Square {
|
|||||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
Ok(vec![(
|
Ok(vec![(
|
||||||
headers::AUTHORIZATION.to_string(),
|
headers::AUTHORIZATION.to_string(),
|
||||||
auth.api_key.expose().into_masked(),
|
format!("Bearer {}", auth.api_key.peek()).into_masked(),
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,16 +100,45 @@ impl ConnectorCommon for Square {
|
|||||||
.parse_struct("SquareErrorResponse")
|
.parse_struct("SquareErrorResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
|
let mut reason_list = Vec::new();
|
||||||
|
for error_iter in response.errors.iter() {
|
||||||
|
if let Some(error) = error_iter.detail.clone() {
|
||||||
|
reason_list.push(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let reason = reason_list.join(" ");
|
||||||
|
|
||||||
Ok(ErrorResponse {
|
Ok(ErrorResponse {
|
||||||
status_code: res.status_code,
|
status_code: res.status_code,
|
||||||
code: response.code,
|
code: response
|
||||||
message: response.message,
|
.errors
|
||||||
reason: response.reason,
|
.first()
|
||||||
|
.and_then(|error| error.code.clone())
|
||||||
|
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||||
|
message: response
|
||||||
|
.errors
|
||||||
|
.first()
|
||||||
|
.and_then(|error| error.category.clone())
|
||||||
|
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||||
|
reason: Some(reason),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectorValidation for Square {}
|
impl ConnectorValidation for Square {
|
||||||
|
fn validate_capture_method(
|
||||||
|
&self,
|
||||||
|
capture_method: Option<enums::CaptureMethod>,
|
||||||
|
) -> CustomResult<(), errors::ConnectorError> {
|
||||||
|
let capture_method = capture_method.unwrap_or_default();
|
||||||
|
match capture_method {
|
||||||
|
enums::CaptureMethod::Automatic | enums::CaptureMethod::Manual => Ok(()),
|
||||||
|
enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err(
|
||||||
|
super::utils::construct_not_implemented_error_report(capture_method, self.id()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||||
for Square
|
for Square
|
||||||
@ -131,6 +156,230 @@ impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::Payments
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl
|
||||||
|
ConnectorIntegration<
|
||||||
|
api::PaymentMethodToken,
|
||||||
|
types::PaymentMethodTokenizationData,
|
||||||
|
types::PaymentsResponseData,
|
||||||
|
> for Square
|
||||||
|
{
|
||||||
|
async fn execute_pretasks(
|
||||||
|
&self,
|
||||||
|
router_data: &mut types::TokenizationRouterData,
|
||||||
|
app_state: &crate::routes::AppState,
|
||||||
|
) -> CustomResult<(), errors::ConnectorError> {
|
||||||
|
let integ: Box<
|
||||||
|
&(dyn ConnectorIntegration<
|
||||||
|
api::AuthorizeSessionToken,
|
||||||
|
types::AuthorizeSessionTokenData,
|
||||||
|
types::PaymentsResponseData,
|
||||||
|
> + Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static),
|
||||||
|
> = Box::new(&Self);
|
||||||
|
|
||||||
|
let authorize_session_token_data = types::AuthorizeSessionTokenData {
|
||||||
|
connector_transaction_id: router_data.payment_id.clone(),
|
||||||
|
amount_to_capture: None,
|
||||||
|
currency: router_data.request.currency,
|
||||||
|
amount: router_data.request.amount,
|
||||||
|
};
|
||||||
|
|
||||||
|
let authorize_data = &types::PaymentsAuthorizeSessionTokenRouterData::from((
|
||||||
|
&router_data.to_owned(),
|
||||||
|
authorize_session_token_data,
|
||||||
|
));
|
||||||
|
|
||||||
|
let resp = services::execute_connector_processing_step(
|
||||||
|
app_state,
|
||||||
|
integ,
|
||||||
|
authorize_data,
|
||||||
|
payments::CallConnectorAction::Trigger,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
router_data.session_token = resp.session_token;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_headers(
|
||||||
|
&self,
|
||||||
|
_req: &types::TokenizationRouterData,
|
||||||
|
_connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||||
|
Ok(vec![(
|
||||||
|
headers::CONTENT_TYPE.to_string(),
|
||||||
|
types::TokenizationType::get_content_type(self)
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_content_type(&self) -> &'static str {
|
||||||
|
self.common_get_content_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_url(
|
||||||
|
&self,
|
||||||
|
_req: &types::TokenizationRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
|
Ok(format!(
|
||||||
|
"{}v2/card-nonce",
|
||||||
|
connectors
|
||||||
|
.square
|
||||||
|
.secondary_base_url
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::ConnectorError::FailedToObtainIntegrationUrl)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_request_body(
|
||||||
|
&self,
|
||||||
|
req: &types::TokenizationRouterData,
|
||||||
|
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||||
|
let connector_request = square::SquareTokenRequest::try_from(req)?;
|
||||||
|
|
||||||
|
let square_req = types::RequestBody::log_and_get_request_body(
|
||||||
|
&connector_request,
|
||||||
|
utils::Encode::<square::SquareTokenRequest>::encode_to_string_of_json,
|
||||||
|
)
|
||||||
|
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||||
|
Ok(Some(square_req))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
req: &types::TokenizationRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||||
|
Ok(Some(
|
||||||
|
services::RequestBuilder::new()
|
||||||
|
.method(services::Method::Post)
|
||||||
|
.url(&types::TokenizationType::get_url(self, req, connectors)?)
|
||||||
|
.attach_default_headers()
|
||||||
|
.headers(types::TokenizationType::get_headers(self, req, connectors)?)
|
||||||
|
.body(types::TokenizationType::get_request_body(self, req)?)
|
||||||
|
.build(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
data: &types::TokenizationRouterData,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<types::TokenizationRouterData, errors::ConnectorError>
|
||||||
|
where
|
||||||
|
types::PaymentsResponseData: Clone,
|
||||||
|
{
|
||||||
|
let response: square::SquareTokenResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("SquareTokenResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
|
response,
|
||||||
|
data: data.clone(),
|
||||||
|
http_code: res.status_code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
self.build_error_response(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl
|
||||||
|
ConnectorIntegration<
|
||||||
|
api::AuthorizeSessionToken,
|
||||||
|
types::AuthorizeSessionTokenData,
|
||||||
|
types::PaymentsResponseData,
|
||||||
|
> for Square
|
||||||
|
{
|
||||||
|
fn get_headers(
|
||||||
|
&self,
|
||||||
|
_req: &types::PaymentsAuthorizeSessionTokenRouterData,
|
||||||
|
_connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||||
|
Ok(vec![(
|
||||||
|
headers::CONTENT_TYPE.to_string(),
|
||||||
|
types::PaymentsAuthorizeType::get_content_type(self)
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_content_type(&self) -> &'static str {
|
||||||
|
self.common_get_content_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_url(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsAuthorizeSessionTokenRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
|
let auth = square::SquareAuthType::try_from(&req.connector_auth_type)
|
||||||
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"{}payments/hydrate?applicationId={}",
|
||||||
|
connectors
|
||||||
|
.square
|
||||||
|
.secondary_base_url
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::ConnectorError::FailedToObtainIntegrationUrl)?,
|
||||||
|
auth.key1.peek()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsAuthorizeSessionTokenRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||||
|
Ok(Some(
|
||||||
|
services::RequestBuilder::new()
|
||||||
|
.method(services::Method::Get)
|
||||||
|
.url(&types::PaymentsPreAuthorizeType::get_url(
|
||||||
|
self, req, connectors,
|
||||||
|
)?)
|
||||||
|
.attach_default_headers()
|
||||||
|
.headers(types::PaymentsPreAuthorizeType::get_headers(
|
||||||
|
self, req, connectors,
|
||||||
|
)?)
|
||||||
|
.build(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
data: &types::PaymentsAuthorizeSessionTokenRouterData,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<types::PaymentsAuthorizeSessionTokenRouterData, errors::ConnectorError> {
|
||||||
|
let response: square::SquareSessionResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("SquareSessionResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
|
response,
|
||||||
|
data: data.clone(),
|
||||||
|
http_code: res.status_code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
self.build_error_response(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||||
for Square
|
for Square
|
||||||
{
|
{
|
||||||
@ -149,9 +398,9 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::PaymentsAuthorizeRouterData,
|
_req: &types::PaymentsAuthorizeRouterData,
|
||||||
_connectors: &settings::Connectors,
|
connectors: &settings::Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
Ok(format!("{}v2/payments", self.base_url(connectors)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_request_body(
|
fn get_request_body(
|
||||||
@ -159,6 +408,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
req: &types::PaymentsAuthorizeRouterData,
|
req: &types::PaymentsAuthorizeRouterData,
|
||||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||||
let req_obj = square::SquarePaymentsRequest::try_from(req)?;
|
let req_obj = square::SquarePaymentsRequest::try_from(req)?;
|
||||||
|
|
||||||
let square_req = types::RequestBody::log_and_get_request_body(
|
let square_req = types::RequestBody::log_and_get_request_body(
|
||||||
&req_obj,
|
&req_obj,
|
||||||
utils::Encode::<square::SquarePaymentsRequest>::encode_to_string_of_json,
|
utils::Encode::<square::SquarePaymentsRequest>::encode_to_string_of_json,
|
||||||
@ -195,7 +445,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
|||||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||||
let response: square::SquarePaymentsResponse = res
|
let response: square::SquarePaymentsResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("Square PaymentsAuthorizeResponse")
|
.parse_struct("SquarePaymentsAuthorizeResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
@ -229,10 +479,19 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
|||||||
|
|
||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::PaymentsSyncRouterData,
|
req: &types::PaymentsSyncRouterData,
|
||||||
_connectors: &settings::Connectors,
|
connectors: &settings::Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
let connector_payment_id = req
|
||||||
|
.request
|
||||||
|
.connector_transaction_id
|
||||||
|
.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"{}v2/payments/{connector_payment_id}",
|
||||||
|
self.base_url(connectors),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_request(
|
fn build_request(
|
||||||
@ -257,7 +516,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
|||||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||||
let response: square::SquarePaymentsResponse = res
|
let response: square::SquarePaymentsResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("square PaymentsSyncResponse")
|
.parse_struct("SquarePaymentsSyncResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
@ -291,17 +550,14 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
|||||||
|
|
||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::PaymentsCaptureRouterData,
|
req: &types::PaymentsCaptureRouterData,
|
||||||
_connectors: &settings::Connectors,
|
connectors: &settings::Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
Ok(format!(
|
||||||
}
|
"{}v2/payments/{}/complete",
|
||||||
|
self.base_url(connectors),
|
||||||
fn get_request_body(
|
req.request.connector_transaction_id,
|
||||||
&self,
|
))
|
||||||
_req: &types::PaymentsCaptureRouterData,
|
|
||||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
|
||||||
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_request(
|
fn build_request(
|
||||||
@ -317,7 +573,6 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
|||||||
.headers(types::PaymentsCaptureType::get_headers(
|
.headers(types::PaymentsCaptureType::get_headers(
|
||||||
self, req, connectors,
|
self, req, connectors,
|
||||||
)?)
|
)?)
|
||||||
.body(types::PaymentsCaptureType::get_request_body(self, req)?)
|
|
||||||
.build(),
|
.build(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -329,7 +584,7 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
|||||||
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
|
||||||
let response: square::SquarePaymentsResponse = res
|
let response: square::SquarePaymentsResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("Square PaymentsCaptureResponse")
|
.parse_struct("SquarePaymentsCaptureResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
@ -349,6 +604,67 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
|||||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||||
for Square
|
for Square
|
||||||
{
|
{
|
||||||
|
fn get_headers(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsCancelRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||||
|
self.build_headers(req, connectors)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_content_type(&self) -> &'static str {
|
||||||
|
self.common_get_content_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_url(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsCancelRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
|
Ok(format!(
|
||||||
|
"{}v2/payments/{}/cancel",
|
||||||
|
self.base_url(connectors),
|
||||||
|
req.request.connector_transaction_id,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
req: &types::PaymentsCancelRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||||
|
Ok(Some(
|
||||||
|
services::RequestBuilder::new()
|
||||||
|
.method(services::Method::Post)
|
||||||
|
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
||||||
|
.attach_default_headers()
|
||||||
|
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
||||||
|
.build(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
data: &types::PaymentsCancelRouterData,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<types::PaymentsCancelRouterData, errors::ConnectorError> {
|
||||||
|
let response: square::SquarePaymentsResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("SquarePaymentsVoidResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
|
response,
|
||||||
|
data: data.clone(),
|
||||||
|
http_code: res.status_code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: Response,
|
||||||
|
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||||
|
self.build_error_response(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Square {
|
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Square {
|
||||||
@ -367,9 +683,9 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
|||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::RefundsRouterData<api::Execute>,
|
_req: &types::RefundsRouterData<api::Execute>,
|
||||||
_connectors: &settings::Connectors,
|
connectors: &settings::Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
Ok(format!("{}v2/refunds", self.base_url(connectors),))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_request_body(
|
fn get_request_body(
|
||||||
@ -407,10 +723,10 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
|
|||||||
data: &types::RefundsRouterData<api::Execute>,
|
data: &types::RefundsRouterData<api::Execute>,
|
||||||
res: Response,
|
res: Response,
|
||||||
) -> CustomResult<types::RefundsRouterData<api::Execute>, errors::ConnectorError> {
|
) -> CustomResult<types::RefundsRouterData<api::Execute>, errors::ConnectorError> {
|
||||||
let response: square::RefundResponse =
|
let response: square::RefundResponse = res
|
||||||
res.response
|
.response
|
||||||
.parse_struct("square RefundResponse")
|
.parse_struct("SquareRefundResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
data: data.clone(),
|
data: data.clone(),
|
||||||
@ -441,10 +757,14 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
|||||||
|
|
||||||
fn get_url(
|
fn get_url(
|
||||||
&self,
|
&self,
|
||||||
_req: &types::RefundSyncRouterData,
|
req: &types::RefundSyncRouterData,
|
||||||
_connectors: &settings::Connectors,
|
connectors: &settings::Connectors,
|
||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
|
Ok(format!(
|
||||||
|
"{}v2/refunds/{}",
|
||||||
|
self.base_url(connectors),
|
||||||
|
req.request.get_connector_refund_id()?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_request(
|
fn build_request(
|
||||||
@ -458,7 +778,6 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
|||||||
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
|
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
|
||||||
.attach_default_headers()
|
.attach_default_headers()
|
||||||
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
|
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
|
||||||
.body(types::RefundSyncType::get_request_body(self, req)?)
|
|
||||||
.build(),
|
.build(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -470,8 +789,9 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
|||||||
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
|
||||||
let response: square::RefundResponse = res
|
let response: square::RefundResponse = res
|
||||||
.response
|
.response
|
||||||
.parse_struct("square RefundSyncResponse")
|
.parse_struct("SquareRefundSyncResponse")
|
||||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
types::RouterData::try_from(types::ResponseRouterData {
|
types::RouterData::try_from(types::ResponseRouterData {
|
||||||
response,
|
response,
|
||||||
data: data.clone(),
|
data: data.clone(),
|
||||||
|
|||||||
@ -1,95 +1,328 @@
|
|||||||
use masking::Secret;
|
use api_models::payments::{BankDebitData, PayLaterData, WalletData};
|
||||||
|
use error_stack::{IntoReport, ResultExt};
|
||||||
|
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connector::utils::PaymentsAuthorizeRequestData,
|
connector::utils::{CardData, PaymentsAuthorizeRequestData, RouterData},
|
||||||
core::errors,
|
core::errors,
|
||||||
types::{self, api, storage::enums},
|
types::{
|
||||||
|
self, api,
|
||||||
|
storage::{self, enums},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: Fill the struct with respective fields
|
impl TryFrom<(&types::TokenizationRouterData, BankDebitData)> for SquareTokenRequest {
|
||||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
pub struct SquarePaymentsRequest {
|
fn try_from(
|
||||||
amount: i64,
|
value: (&types::TokenizationRouterData, BankDebitData),
|
||||||
card: SquareCard,
|
) -> Result<Self, Self::Error> {
|
||||||
|
let (item, bank_debit_data) = value;
|
||||||
|
match bank_debit_data {
|
||||||
|
BankDebitData::AchBankDebit { .. } => Err(errors::ConnectorError::NotImplemented(
|
||||||
|
"Payment Method".to_string(),
|
||||||
|
))
|
||||||
|
.into_report(),
|
||||||
|
_ => Err(errors::ConnectorError::NotSupported {
|
||||||
|
message: format!("{:?}", item.request.payment_method_data),
|
||||||
|
connector: "Square",
|
||||||
|
})?,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
impl TryFrom<(&types::TokenizationRouterData, api_models::payments::Card)> for SquareTokenRequest {
|
||||||
pub struct SquareCard {
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
name: Secret<String>,
|
fn try_from(
|
||||||
|
value: (&types::TokenizationRouterData, api_models::payments::Card),
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
let (item, card_data) = value;
|
||||||
|
let auth = SquareAuthType::try_from(&item.connector_auth_type)
|
||||||
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
|
let exp_year = Secret::new(
|
||||||
|
card_data
|
||||||
|
.get_expiry_year_4_digit()
|
||||||
|
.peek()
|
||||||
|
.parse::<u16>()
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ConnectorError::DateFormattingFailed)?,
|
||||||
|
);
|
||||||
|
let exp_month = Secret::new(
|
||||||
|
card_data
|
||||||
|
.card_exp_month
|
||||||
|
.peek()
|
||||||
|
.parse::<u16>()
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ConnectorError::DateFormattingFailed)?,
|
||||||
|
);
|
||||||
|
//The below error will never happen because if session-id is not generated it would give error in execute_pretasks itself.
|
||||||
|
let session_id = Secret::new(
|
||||||
|
item.session_token
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::ConnectorError::RequestEncodingFailed)?,
|
||||||
|
);
|
||||||
|
Ok(Self::Card(SquareTokenizeData {
|
||||||
|
client_id: auth.key1,
|
||||||
|
session_id,
|
||||||
|
card_data: SquareCardData {
|
||||||
|
exp_year,
|
||||||
|
exp_month,
|
||||||
|
number: card_data.card_number,
|
||||||
|
cvv: card_data.card_cvc,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(&types::TokenizationRouterData, PayLaterData)> for SquareTokenRequest {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(
|
||||||
|
value: (&types::TokenizationRouterData, PayLaterData),
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
let (item, pay_later_data) = value;
|
||||||
|
match pay_later_data {
|
||||||
|
PayLaterData::AfterpayClearpayRedirect { .. } => Err(
|
||||||
|
errors::ConnectorError::NotImplemented("Payment Method".to_string()),
|
||||||
|
)
|
||||||
|
.into_report(),
|
||||||
|
_ => Err(errors::ConnectorError::NotSupported {
|
||||||
|
message: format!("{:?}", item.request.payment_method_data),
|
||||||
|
connector: "Square",
|
||||||
|
})?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(&types::TokenizationRouterData, WalletData)> for SquareTokenRequest {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(value: (&types::TokenizationRouterData, WalletData)) -> Result<Self, Self::Error> {
|
||||||
|
let (item, wallet_data) = value;
|
||||||
|
match wallet_data {
|
||||||
|
WalletData::ApplePay(_) => Err(errors::ConnectorError::NotImplemented(
|
||||||
|
"Payment Method".to_string(),
|
||||||
|
))
|
||||||
|
.into_report(),
|
||||||
|
WalletData::GooglePay(_) => Err(errors::ConnectorError::NotImplemented(
|
||||||
|
"Payment Method".to_string(),
|
||||||
|
))
|
||||||
|
.into_report(),
|
||||||
|
_ => Err(errors::ConnectorError::NotSupported {
|
||||||
|
message: format!("{:?}", item.request.payment_method_data),
|
||||||
|
connector: "Square",
|
||||||
|
})?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct SquareCardData {
|
||||||
|
cvv: Secret<String>,
|
||||||
|
exp_month: Secret<u16>,
|
||||||
|
exp_year: Secret<u16>,
|
||||||
number: cards::CardNumber,
|
number: cards::CardNumber,
|
||||||
expiry_month: Secret<String>,
|
}
|
||||||
expiry_year: Secret<String>,
|
#[derive(Debug, Serialize)]
|
||||||
cvc: Secret<String>,
|
pub struct SquareTokenizeData {
|
||||||
complete: bool,
|
client_id: Secret<String>,
|
||||||
|
session_id: Secret<String>,
|
||||||
|
card_data: SquareCardData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum SquareTokenRequest {
|
||||||
|
Card(SquareTokenizeData),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&types::TokenizationRouterData> for SquareTokenRequest {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(item: &types::TokenizationRouterData) -> Result<Self, Self::Error> {
|
||||||
|
match item.request.payment_method_data.clone() {
|
||||||
|
api::PaymentMethodData::BankDebit(bank_debit_data) => {
|
||||||
|
Self::try_from((item, bank_debit_data))
|
||||||
|
}
|
||||||
|
api::PaymentMethodData::Card(card_data) => Self::try_from((item, card_data)),
|
||||||
|
api::PaymentMethodData::Wallet(wallet_data) => Self::try_from((item, wallet_data)),
|
||||||
|
api::PaymentMethodData::PayLater(pay_later_data) => {
|
||||||
|
Self::try_from((item, pay_later_data))
|
||||||
|
}
|
||||||
|
api::PaymentMethodData::GiftCard(_) => Err(errors::ConnectorError::NotImplemented(
|
||||||
|
"Payment Method".to_string(),
|
||||||
|
))
|
||||||
|
.into_report(),
|
||||||
|
api::PaymentMethodData::BankRedirect(_)
|
||||||
|
| api::PaymentMethodData::BankTransfer(_)
|
||||||
|
| api::PaymentMethodData::CardRedirect(_)
|
||||||
|
| api::PaymentMethodData::Crypto(_)
|
||||||
|
| api::PaymentMethodData::MandatePayment
|
||||||
|
| api::PaymentMethodData::Reward
|
||||||
|
| api::PaymentMethodData::Upi(_)
|
||||||
|
| api::PaymentMethodData::Voucher(_) => Err(errors::ConnectorError::NotSupported {
|
||||||
|
message: format!("{:?}", item.request.payment_method_data),
|
||||||
|
connector: "Square",
|
||||||
|
})?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SquareSessionResponse {
|
||||||
|
session_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T>
|
||||||
|
TryFrom<types::ResponseRouterData<F, SquareSessionResponse, T, types::PaymentsResponseData>>
|
||||||
|
for types::RouterData<F, T, types::PaymentsResponseData>
|
||||||
|
{
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(
|
||||||
|
item: types::ResponseRouterData<F, SquareSessionResponse, T, types::PaymentsResponseData>,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
status: storage::enums::AttemptStatus::Pending,
|
||||||
|
session_token: Some(item.response.session_id.clone()),
|
||||||
|
response: Ok(types::PaymentsResponseData::SessionTokenResponse {
|
||||||
|
session_token: item.response.session_id,
|
||||||
|
}),
|
||||||
|
..item.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct SquareTokenResponse {
|
||||||
|
card_nonce: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T>
|
||||||
|
TryFrom<types::ResponseRouterData<F, SquareTokenResponse, T, types::PaymentsResponseData>>
|
||||||
|
for types::RouterData<F, T, types::PaymentsResponseData>
|
||||||
|
{
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
fn try_from(
|
||||||
|
item: types::ResponseRouterData<F, SquareTokenResponse, T, types::PaymentsResponseData>,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
response: Ok(types::PaymentsResponseData::TokenizationResponse {
|
||||||
|
token: item.response.card_nonce.expose(),
|
||||||
|
}),
|
||||||
|
..item.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct SquarePaymentsAmountData {
|
||||||
|
amount: i64,
|
||||||
|
currency: enums::Currency,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct SquarePaymentsRequestExternalDetails {
|
||||||
|
source: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
source_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct SquarePaymentsRequest {
|
||||||
|
amount_money: SquarePaymentsAmountData,
|
||||||
|
idempotency_key: Secret<String>,
|
||||||
|
source_id: Secret<String>,
|
||||||
|
autocomplete: bool,
|
||||||
|
external_details: SquarePaymentsRequestExternalDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for SquarePaymentsRequest {
|
impl TryFrom<&types::PaymentsAuthorizeRouterData> for SquarePaymentsRequest {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||||
|
let autocomplete = item.request.is_auto_capture()?;
|
||||||
match item.request.payment_method_data.clone() {
|
match item.request.payment_method_data.clone() {
|
||||||
api::PaymentMethodData::Card(req_card) => {
|
api::PaymentMethodData::Card(_) => Ok(Self {
|
||||||
let card = SquareCard {
|
idempotency_key: Secret::new(item.attempt_id.clone()),
|
||||||
name: req_card.card_holder_name,
|
source_id: Secret::new(item.get_payment_method_token()?),
|
||||||
number: req_card.card_number,
|
amount_money: SquarePaymentsAmountData {
|
||||||
expiry_month: req_card.card_exp_month,
|
|
||||||
expiry_year: req_card.card_exp_year,
|
|
||||||
cvc: req_card.card_cvc,
|
|
||||||
complete: item.request.is_auto_capture()?,
|
|
||||||
};
|
|
||||||
Ok(Self {
|
|
||||||
amount: item.request.amount,
|
amount: item.request.amount,
|
||||||
card,
|
currency: item.request.currency,
|
||||||
})
|
},
|
||||||
}
|
autocomplete,
|
||||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
external_details: SquarePaymentsRequestExternalDetails {
|
||||||
|
source: "Hyperswitch".to_string(),
|
||||||
|
source_type: "Card".to_string(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
api::PaymentMethodData::BankDebit(_)
|
||||||
|
| api::PaymentMethodData::GiftCard(_)
|
||||||
|
| api::PaymentMethodData::PayLater(_)
|
||||||
|
| api::PaymentMethodData::Wallet(_) => Err(errors::ConnectorError::NotImplemented(
|
||||||
|
"Payment Method".to_string(),
|
||||||
|
))
|
||||||
|
.into_report(),
|
||||||
|
api::PaymentMethodData::BankRedirect(_)
|
||||||
|
| api::PaymentMethodData::BankTransfer(_)
|
||||||
|
| api::PaymentMethodData::CardRedirect(_)
|
||||||
|
| api::PaymentMethodData::Crypto(_)
|
||||||
|
| api::PaymentMethodData::MandatePayment
|
||||||
|
| api::PaymentMethodData::Reward
|
||||||
|
| api::PaymentMethodData::Upi(_)
|
||||||
|
| api::PaymentMethodData::Voucher(_) => Err(errors::ConnectorError::NotSupported {
|
||||||
|
message: format!("{:?}", item.request.payment_method_data),
|
||||||
|
connector: "Square",
|
||||||
|
})?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Fill the struct with respective fields
|
|
||||||
// Auth Struct
|
// Auth Struct
|
||||||
pub struct SquareAuthType {
|
pub struct SquareAuthType {
|
||||||
pub(super) api_key: Secret<String>,
|
pub(super) api_key: Secret<String>,
|
||||||
|
pub(super) key1: Secret<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&types::ConnectorAuthType> for SquareAuthType {
|
impl TryFrom<&types::ConnectorAuthType> for SquareAuthType {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||||
match auth_type {
|
match auth_type {
|
||||||
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
|
types::ConnectorAuthType::BodyKey { api_key, key1, .. } => Ok(Self {
|
||||||
api_key: api_key.to_owned(),
|
api_key: api_key.to_owned(),
|
||||||
|
key1: key1.to_owned(),
|
||||||
}),
|
}),
|
||||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// PaymentsResponse
|
// PaymentsResponse
|
||||||
//TODO: Append the remaining status flags
|
#[derive(Debug, Deserialize)]
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum SquarePaymentStatus {
|
pub enum SquarePaymentStatus {
|
||||||
Succeeded,
|
Completed,
|
||||||
Failed,
|
Failed,
|
||||||
#[default]
|
Approved,
|
||||||
Processing,
|
Canceled,
|
||||||
|
Pending,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SquarePaymentStatus> for enums::AttemptStatus {
|
impl From<SquarePaymentStatus> for enums::AttemptStatus {
|
||||||
fn from(item: SquarePaymentStatus) -> Self {
|
fn from(item: SquarePaymentStatus) -> Self {
|
||||||
match item {
|
match item {
|
||||||
SquarePaymentStatus::Succeeded => Self::Charged,
|
SquarePaymentStatus::Completed => Self::Charged,
|
||||||
|
SquarePaymentStatus::Approved => Self::Authorized,
|
||||||
SquarePaymentStatus::Failed => Self::Failure,
|
SquarePaymentStatus::Failed => Self::Failure,
|
||||||
SquarePaymentStatus::Processing => Self::Authorizing,
|
SquarePaymentStatus::Canceled => Self::Voided,
|
||||||
|
SquarePaymentStatus::Pending => Self::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Fill the struct with respective fields
|
#[derive(Debug, Deserialize)]
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
pub struct SquarePaymentsResponseDetails {
|
||||||
pub struct SquarePaymentsResponse {
|
|
||||||
status: SquarePaymentStatus,
|
status: SquarePaymentStatus,
|
||||||
id: String,
|
id: String,
|
||||||
|
amount_money: SquarePaymentsAmountData,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct SquarePaymentsResponse {
|
||||||
|
payment: SquarePaymentsResponseDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T>
|
impl<F, T>
|
||||||
@ -101,64 +334,71 @@ impl<F, T>
|
|||||||
item: types::ResponseRouterData<F, SquarePaymentsResponse, T, types::PaymentsResponseData>,
|
item: types::ResponseRouterData<F, SquarePaymentsResponse, T, types::PaymentsResponseData>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
status: enums::AttemptStatus::from(item.response.status),
|
status: enums::AttemptStatus::from(item.response.payment.status),
|
||||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||||
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
|
resource_id: types::ResponseId::ConnectorTransactionId(item.response.payment.id),
|
||||||
redirection_data: None,
|
redirection_data: None,
|
||||||
mandate_reference: None,
|
mandate_reference: None,
|
||||||
connector_metadata: None,
|
connector_metadata: None,
|
||||||
network_txn_id: None,
|
network_txn_id: None,
|
||||||
connector_response_reference_id: None,
|
connector_response_reference_id: None,
|
||||||
}),
|
}),
|
||||||
|
amount_captured: Some(item.response.payment.amount_money.amount),
|
||||||
..item.data
|
..item.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Fill the struct with respective fields
|
|
||||||
// REFUND :
|
// REFUND :
|
||||||
// Type definition for RefundRequest
|
// Type definition for RefundRequest
|
||||||
#[derive(Default, Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct SquareRefundRequest {
|
pub struct SquareRefundRequest {
|
||||||
pub amount: i64,
|
amount_money: SquarePaymentsAmountData,
|
||||||
|
idempotency_key: Secret<String>,
|
||||||
|
payment_id: Secret<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> TryFrom<&types::RefundsRouterData<F>> for SquareRefundRequest {
|
impl<F> TryFrom<&types::RefundsRouterData<F>> for SquareRefundRequest {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
amount: item.request.refund_amount,
|
amount_money: SquarePaymentsAmountData {
|
||||||
|
amount: item.request.refund_amount,
|
||||||
|
currency: item.request.currency,
|
||||||
|
},
|
||||||
|
idempotency_key: Secret::new(item.request.refund_id.clone()),
|
||||||
|
payment_id: Secret::new(item.request.connector_transaction_id.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type definition for Refund Response
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
|
|
||||||
pub enum RefundStatus {
|
pub enum RefundStatus {
|
||||||
Succeeded,
|
Completed,
|
||||||
Failed,
|
Failed,
|
||||||
#[default]
|
Pending,
|
||||||
Processing,
|
Rejected,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RefundStatus> for enums::RefundStatus {
|
impl From<RefundStatus> for enums::RefundStatus {
|
||||||
fn from(item: RefundStatus) -> Self {
|
fn from(item: RefundStatus) -> Self {
|
||||||
match item {
|
match item {
|
||||||
RefundStatus::Succeeded => Self::Success,
|
RefundStatus::Completed => Self::Success,
|
||||||
RefundStatus::Failed => Self::Failure,
|
RefundStatus::Failed | RefundStatus::Rejected => Self::Failure,
|
||||||
RefundStatus::Processing => Self::Pending,
|
RefundStatus::Pending => Self::Pending,
|
||||||
//TODO: Review mapping
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Fill the struct with respective fields
|
#[derive(Debug, Deserialize)]
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
pub struct SquareRefundResponseDetails {
|
||||||
pub struct RefundResponse {
|
|
||||||
id: String,
|
|
||||||
status: RefundStatus,
|
status: RefundStatus,
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct RefundResponse {
|
||||||
|
refund: SquareRefundResponseDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
||||||
@ -170,8 +410,8 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
|
|||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
response: Ok(types::RefundsResponseData {
|
response: Ok(types::RefundsResponseData {
|
||||||
connector_refund_id: item.response.id.to_string(),
|
connector_refund_id: item.response.refund.id,
|
||||||
refund_status: enums::RefundStatus::from(item.response.status),
|
refund_status: enums::RefundStatus::from(item.response.refund.status),
|
||||||
}),
|
}),
|
||||||
..item.data
|
..item.data
|
||||||
})
|
})
|
||||||
@ -187,19 +427,21 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
|
|||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
response: Ok(types::RefundsResponseData {
|
response: Ok(types::RefundsResponseData {
|
||||||
connector_refund_id: item.response.id.to_string(),
|
connector_refund_id: item.response.refund.id,
|
||||||
refund_status: enums::RefundStatus::from(item.response.status),
|
refund_status: enums::RefundStatus::from(item.response.refund.status),
|
||||||
}),
|
}),
|
||||||
..item.data
|
..item.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Fill the struct with respective fields
|
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
|
pub struct SquareErrorDetails {
|
||||||
pub struct SquareErrorResponse {
|
pub category: Option<String>,
|
||||||
pub status_code: u16,
|
pub code: Option<String>,
|
||||||
pub code: String,
|
pub detail: Option<String>,
|
||||||
pub message: String,
|
}
|
||||||
pub reason: Option<String>,
|
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct SquareErrorResponse {
|
||||||
|
pub errors: Vec<SquareErrorDetails>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1219,6 +1219,10 @@ pub(crate) fn validate_auth_type(
|
|||||||
shift4::transformers::Shift4AuthType::try_from(val)?;
|
shift4::transformers::Shift4AuthType::try_from(val)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
api_enums::Connector::Square => {
|
||||||
|
square::transformers::SquareAuthType::try_from(val)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
api_enums::Connector::Stax => {
|
api_enums::Connector::Stax => {
|
||||||
stax::transformers::StaxAuthType::try_from(val)?;
|
stax::transformers::StaxAuthType::try_from(val)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -59,7 +59,7 @@ pub trait Feature<F, T> {
|
|||||||
dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>;
|
dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>;
|
||||||
|
|
||||||
async fn add_payment_method_token<'a>(
|
async fn add_payment_method_token<'a>(
|
||||||
&self,
|
&mut self,
|
||||||
_state: &AppState,
|
_state: &AppState,
|
||||||
_connector: &api::ConnectorData,
|
_connector: &api::ConnectorData,
|
||||||
_tokenization_action: &payments::TokenizationAction,
|
_tokenization_action: &payments::TokenizationAction,
|
||||||
|
|||||||
@ -118,17 +118,18 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn add_payment_method_token<'a>(
|
async fn add_payment_method_token<'a>(
|
||||||
&self,
|
&mut self,
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
tokenization_action: &payments::TokenizationAction,
|
tokenization_action: &payments::TokenizationAction,
|
||||||
) -> RouterResult<Option<String>> {
|
) -> RouterResult<Option<String>> {
|
||||||
|
let request = self.request.clone();
|
||||||
tokenization::add_payment_method_token(
|
tokenization::add_payment_method_token(
|
||||||
state,
|
state,
|
||||||
connector,
|
connector,
|
||||||
tokenization_action,
|
tokenization_action,
|
||||||
self,
|
self,
|
||||||
types::PaymentMethodTokenizationData::try_from(self.request.to_owned())?,
|
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -346,6 +347,8 @@ impl TryFrom<types::PaymentsAuthorizeData> for types::PaymentMethodTokenizationD
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
payment_method_data: data.payment_method_data,
|
payment_method_data: data.payment_method_data,
|
||||||
browser_info: data.browser_info,
|
browser_info: data.browser_info,
|
||||||
|
currency: data.currency,
|
||||||
|
amount: Some(data.amount),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,17 +86,18 @@ impl Feature<api::Verify, types::VerifyRequestData> for types::VerifyRouterData
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn add_payment_method_token<'a>(
|
async fn add_payment_method_token<'a>(
|
||||||
&self,
|
&mut self,
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
tokenization_action: &payments::TokenizationAction,
|
tokenization_action: &payments::TokenizationAction,
|
||||||
) -> RouterResult<Option<String>> {
|
) -> RouterResult<Option<String>> {
|
||||||
|
let request = self.request.clone();
|
||||||
tokenization::add_payment_method_token(
|
tokenization::add_payment_method_token(
|
||||||
state,
|
state,
|
||||||
connector,
|
connector,
|
||||||
tokenization_action,
|
tokenization_action,
|
||||||
self,
|
self,
|
||||||
types::PaymentMethodTokenizationData::try_from(self.request.to_owned())?,
|
types::PaymentMethodTokenizationData::try_from(request)?,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -234,6 +235,8 @@ impl TryFrom<types::VerifyRequestData> for types::PaymentMethodTokenizationData
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
payment_method_data: data.payment_method_data,
|
payment_method_data: data.payment_method_data,
|
||||||
browser_info: None,
|
browser_info: None,
|
||||||
|
currency: data.currency,
|
||||||
|
amount: data.amount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,11 +211,11 @@ pub fn create_payment_method_metadata(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_payment_method_token<F: Clone, T: Clone>(
|
pub async fn add_payment_method_token<F: Clone, T: types::Tokenizable + Clone>(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
connector: &api::ConnectorData,
|
connector: &api::ConnectorData,
|
||||||
tokenization_action: &payments::TokenizationAction,
|
tokenization_action: &payments::TokenizationAction,
|
||||||
router_data: &types::RouterData<F, T, types::PaymentsResponseData>,
|
router_data: &mut types::RouterData<F, T, types::PaymentsResponseData>,
|
||||||
pm_token_request_data: types::PaymentMethodTokenizationData,
|
pm_token_request_data: types::PaymentMethodTokenizationData,
|
||||||
) -> RouterResult<Option<String>> {
|
) -> RouterResult<Option<String>> {
|
||||||
match tokenization_action {
|
match tokenization_action {
|
||||||
@ -230,7 +230,7 @@ pub async fn add_payment_method_token<F: Clone, T: Clone>(
|
|||||||
let pm_token_response_data: Result<types::PaymentsResponseData, types::ErrorResponse> =
|
let pm_token_response_data: Result<types::PaymentsResponseData, types::ErrorResponse> =
|
||||||
Err(types::ErrorResponse::default());
|
Err(types::ErrorResponse::default());
|
||||||
|
|
||||||
let pm_token_router_data = payments::helpers::router_data_type_conversion::<
|
let mut pm_token_router_data = payments::helpers::router_data_type_conversion::<
|
||||||
_,
|
_,
|
||||||
api::PaymentMethodToken,
|
api::PaymentMethodToken,
|
||||||
_,
|
_,
|
||||||
@ -242,6 +242,16 @@ pub async fn add_payment_method_token<F: Clone, T: Clone>(
|
|||||||
pm_token_request_data,
|
pm_token_request_data,
|
||||||
pm_token_response_data,
|
pm_token_response_data,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
connector_integration
|
||||||
|
.execute_pretasks(&mut pm_token_router_data, state)
|
||||||
|
.await
|
||||||
|
.to_payment_failed_response()?;
|
||||||
|
|
||||||
|
router_data
|
||||||
|
.request
|
||||||
|
.set_session_token(pm_token_router_data.session_token.clone());
|
||||||
|
|
||||||
let resp = services::execute_connector_processing_step(
|
let resp = services::execute_connector_processing_step(
|
||||||
state,
|
state,
|
||||||
connector_integration,
|
connector_integration,
|
||||||
|
|||||||
@ -1129,6 +1129,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::VerifyRequestDat
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
currency: payment_data.currency,
|
currency: payment_data.currency,
|
||||||
confirm: true,
|
confirm: true,
|
||||||
|
amount: Some(payment_data.amount.into()),
|
||||||
payment_method_data: payment_data
|
payment_method_data: payment_data
|
||||||
.payment_method_data
|
.payment_method_data
|
||||||
.get_required_value("payment_method_data")?,
|
.get_required_value("payment_method_data")?,
|
||||||
|
|||||||
@ -351,7 +351,7 @@ pub struct AuthorizeSessionTokenData {
|
|||||||
pub amount_to_capture: Option<i64>,
|
pub amount_to_capture: Option<i64>,
|
||||||
pub currency: storage_enums::Currency,
|
pub currency: storage_enums::Currency,
|
||||||
pub connector_transaction_id: String,
|
pub connector_transaction_id: String,
|
||||||
pub amount: i64,
|
pub amount: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -367,6 +367,8 @@ pub struct ConnectorCustomerData {
|
|||||||
pub struct PaymentMethodTokenizationData {
|
pub struct PaymentMethodTokenizationData {
|
||||||
pub payment_method_data: payments::PaymentMethodData,
|
pub payment_method_data: payments::PaymentMethodData,
|
||||||
pub browser_info: Option<BrowserInformation>,
|
pub browser_info: Option<BrowserInformation>,
|
||||||
|
pub currency: storage_enums::Currency,
|
||||||
|
pub amount: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -448,6 +450,7 @@ pub struct PaymentsSessionData {
|
|||||||
pub struct VerifyRequestData {
|
pub struct VerifyRequestData {
|
||||||
pub currency: storage_enums::Currency,
|
pub currency: storage_enums::Currency,
|
||||||
pub payment_method_data: payments::PaymentMethodData,
|
pub payment_method_data: payments::PaymentMethodData,
|
||||||
|
pub amount: Option<i64>,
|
||||||
pub confirm: bool,
|
pub confirm: bool,
|
||||||
pub statement_descriptor_suffix: Option<String>,
|
pub statement_descriptor_suffix: Option<String>,
|
||||||
pub mandate_id: Option<api_models::payments::MandateIds>,
|
pub mandate_id: Option<api_models::payments::MandateIds>,
|
||||||
@ -874,7 +877,7 @@ impl From<&&mut PaymentsAuthorizeRouterData> for AuthorizeSessionTokenData {
|
|||||||
amount_to_capture: data.amount_captured,
|
amount_to_capture: data.amount_captured,
|
||||||
currency: data.request.currency,
|
currency: data.request.currency,
|
||||||
connector_transaction_id: data.payment_id.clone(),
|
connector_transaction_id: data.payment_id.clone(),
|
||||||
amount: data.request.amount,
|
amount: Some(data.request.amount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -891,6 +894,40 @@ impl From<&&mut PaymentsAuthorizeRouterData> for ConnectorCustomerData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F> From<&RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>>
|
||||||
|
for PaymentMethodTokenizationData
|
||||||
|
{
|
||||||
|
fn from(data: &RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>) -> Self {
|
||||||
|
Self {
|
||||||
|
payment_method_data: data.request.payment_method_data.clone(),
|
||||||
|
browser_info: None,
|
||||||
|
currency: data.request.currency,
|
||||||
|
amount: Some(data.request.amount),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Tokenizable {
|
||||||
|
fn get_pm_data(&self) -> payments::PaymentMethodData;
|
||||||
|
fn set_session_token(&mut self, token: Option<String>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tokenizable for VerifyRequestData {
|
||||||
|
fn get_pm_data(&self) -> payments::PaymentMethodData {
|
||||||
|
self.payment_method_data.clone()
|
||||||
|
}
|
||||||
|
fn set_session_token(&mut self, _token: Option<String>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tokenizable for PaymentsAuthorizeData {
|
||||||
|
fn get_pm_data(&self) -> payments::PaymentMethodData {
|
||||||
|
self.payment_method_data.clone()
|
||||||
|
}
|
||||||
|
fn set_session_token(&mut self, token: Option<String>) {
|
||||||
|
self.session_token = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&VerifyRouterData> for PaymentsAuthorizeData {
|
impl From<&VerifyRouterData> for PaymentsAuthorizeData {
|
||||||
fn from(data: &VerifyRouterData) -> Self {
|
fn from(data: &VerifyRouterData) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@ -319,7 +319,7 @@ impl ConnectorData {
|
|||||||
enums::Connector::Powertranz => Ok(Box::new(&connector::Powertranz)),
|
enums::Connector::Powertranz => Ok(Box::new(&connector::Powertranz)),
|
||||||
enums::Connector::Rapyd => Ok(Box::new(&connector::Rapyd)),
|
enums::Connector::Rapyd => Ok(Box::new(&connector::Rapyd)),
|
||||||
enums::Connector::Shift4 => Ok(Box::new(&connector::Shift4)),
|
enums::Connector::Shift4 => Ok(Box::new(&connector::Shift4)),
|
||||||
//enums::Connector::Square => Ok(Box::new(&connector::Square)), it is added as template code for future usage
|
enums::Connector::Square => Ok(Box::new(&connector::Square)),
|
||||||
enums::Connector::Stax => Ok(Box::new(&connector::Stax)),
|
enums::Connector::Stax => Ok(Box::new(&connector::Stax)),
|
||||||
enums::Connector::Stripe => Ok(Box::new(&connector::Stripe)),
|
enums::Connector::Stripe => Ok(Box::new(&connector::Stripe)),
|
||||||
enums::Connector::Wise => Ok(Box::new(&connector::Wise)),
|
enums::Connector::Wise => Ok(Box::new(&connector::Wise)),
|
||||||
|
|||||||
@ -171,6 +171,7 @@ key1 = "transaction key"
|
|||||||
|
|
||||||
[square]
|
[square]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
|
key1 = "transaction key"
|
||||||
|
|
||||||
[helcim]
|
[helcim]
|
||||||
api_key="API Key"
|
api_key="API Key"
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
|
use std::{str::FromStr, time::Duration};
|
||||||
|
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
use router::types::{self, api, storage::enums};
|
use router::types::{
|
||||||
|
self, api,
|
||||||
|
storage::{self, enums},
|
||||||
|
PaymentsResponseData,
|
||||||
|
};
|
||||||
use test_utils::connector_auth::ConnectorAuthentication;
|
use test_utils::connector_auth::ConnectorAuthentication;
|
||||||
|
|
||||||
use crate::utils::{self, ConnectorActions};
|
use crate::utils::{self, get_connector_transaction_id, Connector, ConnectorActions};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct SquareTest;
|
struct SquareTest;
|
||||||
impl ConnectorActions for SquareTest {}
|
impl ConnectorActions for SquareTest {}
|
||||||
impl utils::Connector for SquareTest {
|
impl Connector for SquareTest {
|
||||||
fn get_data(&self) -> types::api::ConnectorData {
|
fn get_data(&self) -> types::api::ConnectorData {
|
||||||
use router::connector::Square;
|
use router::connector::Square;
|
||||||
types::api::ConnectorData {
|
types::api::ConnectorData {
|
||||||
connector: Box::new(&Square),
|
connector: Box::new(&Square),
|
||||||
connector_name: types::Connector::DummyConnector1,
|
connector_name: types::Connector::Square,
|
||||||
get_token: types::api::GetToken::Connector,
|
get_token: types::api::GetToken::Connector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,20 +39,60 @@ impl utils::Connector for SquareTest {
|
|||||||
|
|
||||||
static CONNECTOR: SquareTest = SquareTest {};
|
static CONNECTOR: SquareTest = SquareTest {};
|
||||||
|
|
||||||
fn get_default_payment_info() -> Option<utils::PaymentInfo> {
|
fn get_default_payment_info(payment_method_token: Option<String>) -> Option<utils::PaymentInfo> {
|
||||||
None
|
Some(utils::PaymentInfo {
|
||||||
|
address: None,
|
||||||
|
auth_type: None,
|
||||||
|
access_token: None,
|
||||||
|
connector_meta_data: None,
|
||||||
|
return_url: None,
|
||||||
|
connector_customer: None,
|
||||||
|
payment_method_token,
|
||||||
|
payout_method_data: None,
|
||||||
|
currency: None,
|
||||||
|
country: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn token_details() -> Option<types::PaymentMethodTokenizationData> {
|
||||||
|
Some(types::PaymentMethodTokenizationData {
|
||||||
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: cards::CardNumber::from_str("4111111111111111").unwrap(),
|
||||||
|
card_exp_month: Secret::new("04".to_string()),
|
||||||
|
card_exp_year: Secret::new("2027".to_string()),
|
||||||
|
card_cvc: Secret::new("100".to_string()),
|
||||||
|
..utils::CCardType::default().0
|
||||||
|
}),
|
||||||
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: storage::enums::Currency::USD,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_token() -> Option<String> {
|
||||||
|
let token_response = CONNECTOR
|
||||||
|
.create_connector_pm_token(token_details(), get_default_payment_info(None))
|
||||||
|
.await
|
||||||
|
.expect("Authorize payment response");
|
||||||
|
match token_response.response.unwrap() {
|
||||||
|
PaymentsResponseData::TokenizationResponse { token } => Some(token),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cards Positive Tests
|
// Cards Positive Tests
|
||||||
// Creates a payment using the manual capture flow (Non 3DS).
|
// Creates a payment using the manual capture flow (Non 3DS).
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_only_authorize_payment() {
|
async fn should_only_authorize_payment() {
|
||||||
let response = CONNECTOR
|
let response = CONNECTOR
|
||||||
.authorize_payment(payment_method_details(), get_default_payment_info())
|
.authorize_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Authorize payment response");
|
.expect("Authorize payment response");
|
||||||
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
assert_eq!(response.status, enums::AttemptStatus::Authorized);
|
||||||
@ -56,7 +102,11 @@ async fn should_only_authorize_payment() {
|
|||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_capture_authorized_payment() {
|
async fn should_capture_authorized_payment() {
|
||||||
let response = CONNECTOR
|
let response = CONNECTOR
|
||||||
.authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
|
.authorize_and_capture_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Capture payment response");
|
.expect("Capture payment response");
|
||||||
assert_eq!(response.status, enums::AttemptStatus::Charged);
|
assert_eq!(response.status, enums::AttemptStatus::Charged);
|
||||||
@ -72,7 +122,7 @@ async fn should_partially_capture_authorized_payment() {
|
|||||||
amount_to_capture: 50,
|
amount_to_capture: 50,
|
||||||
..utils::PaymentCaptureType::default().0
|
..utils::PaymentCaptureType::default().0
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Capture payment response");
|
.expect("Capture payment response");
|
||||||
@ -83,10 +133,13 @@ async fn should_partially_capture_authorized_payment() {
|
|||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_sync_authorized_payment() {
|
async fn should_sync_authorized_payment() {
|
||||||
let authorize_response = CONNECTOR
|
let authorize_response = CONNECTOR
|
||||||
.authorize_payment(payment_method_details(), get_default_payment_info())
|
.authorize_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Authorize payment response");
|
.expect("Authorize payment response");
|
||||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
let txn_id = get_connector_transaction_id(authorize_response.response);
|
||||||
let response = CONNECTOR
|
let response = CONNECTOR
|
||||||
.psync_retry_till_status_matches(
|
.psync_retry_till_status_matches(
|
||||||
enums::AttemptStatus::Authorized,
|
enums::AttemptStatus::Authorized,
|
||||||
@ -96,7 +149,7 @@ async fn should_sync_authorized_payment() {
|
|||||||
),
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("PSync response");
|
.expect("PSync response");
|
||||||
@ -114,7 +167,7 @@ async fn should_void_authorized_payment() {
|
|||||||
cancellation_reason: Some("requested_by_customer".to_string()),
|
cancellation_reason: Some("requested_by_customer".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Void payment response");
|
.expect("Void payment response");
|
||||||
@ -124,12 +177,21 @@ async fn should_void_authorized_payment() {
|
|||||||
// Refunds a payment using the manual capture flow (Non 3DS).
|
// Refunds a payment using the manual capture flow (Non 3DS).
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_refund_manually_captured_payment() {
|
async fn should_refund_manually_captured_payment() {
|
||||||
let response = CONNECTOR
|
let refund_response = CONNECTOR
|
||||||
.capture_payment_and_refund(
|
.capture_payment_and_refund(
|
||||||
payment_method_details(),
|
payment_method_details(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let response = CONNECTOR
|
||||||
|
.rsync_retry_till_status_matches(
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
|
None,
|
||||||
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -142,7 +204,7 @@ async fn should_refund_manually_captured_payment() {
|
|||||||
// Partially refunds a payment using the manual capture flow (Non 3DS).
|
// Partially refunds a payment using the manual capture flow (Non 3DS).
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_partially_refund_manually_captured_payment() {
|
async fn should_partially_refund_manually_captured_payment() {
|
||||||
let response = CONNECTOR
|
let refund_response = CONNECTOR
|
||||||
.capture_payment_and_refund(
|
.capture_payment_and_refund(
|
||||||
payment_method_details(),
|
payment_method_details(),
|
||||||
None,
|
None,
|
||||||
@ -150,7 +212,16 @@ async fn should_partially_refund_manually_captured_payment() {
|
|||||||
refund_amount: 50,
|
refund_amount: 50,
|
||||||
..utils::PaymentRefundType::default().0
|
..utils::PaymentRefundType::default().0
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let response = CONNECTOR
|
||||||
|
.rsync_retry_till_status_matches(
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
|
None,
|
||||||
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -168,7 +239,7 @@ async fn should_sync_manually_captured_refund() {
|
|||||||
payment_method_details(),
|
payment_method_details(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -177,7 +248,7 @@ async fn should_sync_manually_captured_refund() {
|
|||||||
enums::RefundStatus::Success,
|
enums::RefundStatus::Success,
|
||||||
refund_response.response.unwrap().connector_refund_id,
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
None,
|
None,
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -191,7 +262,10 @@ async fn should_sync_manually_captured_refund() {
|
|||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_make_payment() {
|
async fn should_make_payment() {
|
||||||
let authorize_response = CONNECTOR
|
let authorize_response = CONNECTOR
|
||||||
.make_payment(payment_method_details(), get_default_payment_info())
|
.make_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||||
@ -201,11 +275,14 @@ async fn should_make_payment() {
|
|||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_sync_auto_captured_payment() {
|
async fn should_sync_auto_captured_payment() {
|
||||||
let authorize_response = CONNECTOR
|
let authorize_response = CONNECTOR
|
||||||
.make_payment(payment_method_details(), get_default_payment_info())
|
.make_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
let txn_id = get_connector_transaction_id(authorize_response.response);
|
||||||
assert_ne!(txn_id, None, "Empty connector transaction id");
|
assert_ne!(txn_id, None, "Empty connector transaction id");
|
||||||
let response = CONNECTOR
|
let response = CONNECTOR
|
||||||
.psync_retry_till_status_matches(
|
.psync_retry_till_status_matches(
|
||||||
@ -217,7 +294,7 @@ async fn should_sync_auto_captured_payment() {
|
|||||||
capture_method: Some(enums::CaptureMethod::Automatic),
|
capture_method: Some(enums::CaptureMethod::Automatic),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -227,8 +304,21 @@ async fn should_sync_auto_captured_payment() {
|
|||||||
// Refunds a payment using the automatic capture flow (Non 3DS).
|
// Refunds a payment using the automatic capture flow (Non 3DS).
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_refund_auto_captured_payment() {
|
async fn should_refund_auto_captured_payment() {
|
||||||
|
let refund_response = CONNECTOR
|
||||||
|
.make_payment_and_refund(
|
||||||
|
payment_method_details(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
let response = CONNECTOR
|
let response = CONNECTOR
|
||||||
.make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
|
.rsync_retry_till_status_matches(
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
|
None,
|
||||||
|
get_default_payment_info(None),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -247,44 +337,85 @@ async fn should_partially_refund_succeeded_payment() {
|
|||||||
refund_amount: 50,
|
refund_amount: 50,
|
||||||
..utils::PaymentRefundType::default().0
|
..utils::PaymentRefundType::default().0
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
|
||||||
refund_response.response.unwrap().refund_status,
|
|
||||||
enums::RefundStatus::Success,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
|
|
||||||
#[actix_web::test]
|
|
||||||
async fn should_refund_succeeded_payment_multiple_times() {
|
|
||||||
CONNECTOR
|
|
||||||
.make_payment_and_multiple_refund(
|
|
||||||
payment_method_details(),
|
|
||||||
Some(types::RefundsData {
|
|
||||||
refund_amount: 50,
|
|
||||||
..utils::PaymentRefundType::default().0
|
|
||||||
}),
|
|
||||||
get_default_payment_info(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronizes a refund using the automatic capture flow (Non 3DS).
|
|
||||||
#[actix_web::test]
|
|
||||||
async fn should_sync_refund() {
|
|
||||||
let refund_response = CONNECTOR
|
|
||||||
.make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let response = CONNECTOR
|
let response = CONNECTOR
|
||||||
.rsync_retry_till_status_matches(
|
.rsync_retry_till_status_matches(
|
||||||
enums::RefundStatus::Success,
|
enums::RefundStatus::Success,
|
||||||
refund_response.response.unwrap().connector_refund_id,
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
None,
|
None,
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
response.response.unwrap().refund_status,
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_refund_succeeded_payment_multiple_times() {
|
||||||
|
//make a successful payment
|
||||||
|
let response = CONNECTOR
|
||||||
|
.make_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let refund_data = Some(types::RefundsData {
|
||||||
|
refund_amount: 50,
|
||||||
|
..utils::PaymentRefundType::default().0
|
||||||
|
});
|
||||||
|
//try refund for previous payment
|
||||||
|
let transaction_id = get_connector_transaction_id(response.response).unwrap();
|
||||||
|
for _x in 0..2 {
|
||||||
|
tokio::time::sleep(Duration::from_secs(CONNECTOR.get_request_interval())).await; // to avoid 404 error
|
||||||
|
let refund_response = CONNECTOR
|
||||||
|
.refund_payment(
|
||||||
|
transaction_id.clone(),
|
||||||
|
refund_data.clone(),
|
||||||
|
get_default_payment_info(None),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let response = CONNECTOR
|
||||||
|
.rsync_retry_till_status_matches(
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
|
None,
|
||||||
|
get_default_payment_info(None),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
response.response.unwrap().refund_status,
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronizes a refund using the automatic capture flow (Non 3DS).
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn should_sync_refund() {
|
||||||
|
let refund_response = CONNECTOR
|
||||||
|
.make_payment_and_refund(
|
||||||
|
payment_method_details(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let response = CONNECTOR
|
||||||
|
.rsync_retry_till_status_matches(
|
||||||
|
enums::RefundStatus::Success,
|
||||||
|
refund_response.response.unwrap().connector_refund_id,
|
||||||
|
None,
|
||||||
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -298,66 +429,93 @@ async fn should_sync_refund() {
|
|||||||
// Creates a payment with incorrect CVC.
|
// Creates a payment with incorrect CVC.
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_fail_payment_for_incorrect_cvc() {
|
async fn should_fail_payment_for_incorrect_cvc() {
|
||||||
let response = CONNECTOR
|
let token_response = CONNECTOR
|
||||||
.make_payment(
|
.create_connector_pm_token(
|
||||||
Some(types::PaymentsAuthorizeData {
|
Some(types::PaymentMethodTokenizationData {
|
||||||
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
card_cvc: Secret::new("12345".to_string()),
|
card_number: cards::CardNumber::from_str("4111111111111111").unwrap(),
|
||||||
|
card_exp_month: Secret::new("11".to_string()),
|
||||||
|
card_exp_year: Secret::new("2027".to_string()),
|
||||||
|
card_cvc: Secret::new("".to_string()),
|
||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
..utils::PaymentAuthorizeType::default().0
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: storage::enums::Currency::USD,
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("Authorize payment response");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response.response.unwrap_err().message,
|
token_response
|
||||||
"Your card's security code is invalid.".to_string(),
|
.response
|
||||||
|
.unwrap_err()
|
||||||
|
.reason
|
||||||
|
.unwrap_or("".to_string()),
|
||||||
|
"Missing required parameter.".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a payment with incorrect expiry month.
|
// Creates a payment with incorrect expiry month.
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_fail_payment_for_invalid_exp_month() {
|
async fn should_fail_payment_for_invalid_exp_month() {
|
||||||
let response = CONNECTOR
|
let token_response = CONNECTOR
|
||||||
.make_payment(
|
.create_connector_pm_token(
|
||||||
Some(types::PaymentsAuthorizeData {
|
Some(types::PaymentMethodTokenizationData {
|
||||||
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: cards::CardNumber::from_str("4111111111111111").unwrap(),
|
||||||
card_exp_month: Secret::new("20".to_string()),
|
card_exp_month: Secret::new("20".to_string()),
|
||||||
|
card_exp_year: Secret::new("2027".to_string()),
|
||||||
|
card_cvc: Secret::new("123".to_string()),
|
||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
..utils::PaymentAuthorizeType::default().0
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: storage::enums::Currency::USD,
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("Authorize payment response");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response.response.unwrap_err().message,
|
token_response
|
||||||
"Your card's expiration month is invalid.".to_string(),
|
.response
|
||||||
|
.unwrap_err()
|
||||||
|
.reason
|
||||||
|
.unwrap_or("".to_string()),
|
||||||
|
"Invalid card expiration date.".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a payment with incorrect expiry year.
|
// Creates a payment with incorrect expiry year.
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_fail_payment_for_incorrect_expiry_year() {
|
async fn should_fail_payment_for_incorrect_expiry_year() {
|
||||||
let response = CONNECTOR
|
let token_response = CONNECTOR
|
||||||
.make_payment(
|
.create_connector_pm_token(
|
||||||
Some(types::PaymentsAuthorizeData {
|
Some(types::PaymentMethodTokenizationData {
|
||||||
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
|
||||||
|
card_number: cards::CardNumber::from_str("4111111111111111").unwrap(),
|
||||||
|
card_exp_month: Secret::new("11".to_string()),
|
||||||
card_exp_year: Secret::new("2000".to_string()),
|
card_exp_year: Secret::new("2000".to_string()),
|
||||||
|
card_cvc: Secret::new("123".to_string()),
|
||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
..utils::PaymentAuthorizeType::default().0
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: storage::enums::Currency::USD,
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(None),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("Authorize payment response");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response.response.unwrap_err().message,
|
token_response
|
||||||
"Your card's expiration year is invalid.".to_string(),
|
.response
|
||||||
|
.unwrap_err()
|
||||||
|
.reason
|
||||||
|
.unwrap_or("".to_string()),
|
||||||
|
"Invalid card expiration date.".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,19 +523,27 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
|
|||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_fail_void_payment_for_auto_capture() {
|
async fn should_fail_void_payment_for_auto_capture() {
|
||||||
let authorize_response = CONNECTOR
|
let authorize_response = CONNECTOR
|
||||||
.make_payment(payment_method_details(), get_default_payment_info())
|
.make_payment(
|
||||||
|
payment_method_details(),
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
|
||||||
let txn_id = utils::get_connector_transaction_id(authorize_response.response);
|
let txn_id = get_connector_transaction_id(authorize_response.response);
|
||||||
assert_ne!(txn_id, None, "Empty connector transaction id");
|
assert_ne!(txn_id, None, "Empty connector transaction id");
|
||||||
let void_response = CONNECTOR
|
let void_response = CONNECTOR
|
||||||
.void_payment(txn_id.unwrap(), None, get_default_payment_info())
|
.void_payment(
|
||||||
|
txn_id.clone().unwrap(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(None),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let connector_transaction_id = txn_id.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
void_response.response.unwrap_err().message,
|
void_response.response.unwrap_err().reason.unwrap_or("".to_string()),
|
||||||
"You cannot cancel this PaymentIntent because it has a status of succeeded."
|
format!("Payment {connector_transaction_id} is in inflight state COMPLETED, which is invalid for the requested operation")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,12 +551,20 @@ async fn should_fail_void_payment_for_auto_capture() {
|
|||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn should_fail_capture_for_invalid_payment() {
|
async fn should_fail_capture_for_invalid_payment() {
|
||||||
let capture_response = CONNECTOR
|
let capture_response = CONNECTOR
|
||||||
.capture_payment("123456789".to_string(), None, get_default_payment_info())
|
.capture_payment(
|
||||||
|
"123456789".to_string(),
|
||||||
|
None,
|
||||||
|
get_default_payment_info(create_token().await),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
capture_response.response.unwrap_err().message,
|
capture_response
|
||||||
String::from("No such payment_intent: '123456789'")
|
.response
|
||||||
|
.unwrap_err()
|
||||||
|
.reason
|
||||||
|
.unwrap_or("".to_string()),
|
||||||
|
String::from("Could not find payment with id: 123456789")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,13 +578,17 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() {
|
|||||||
refund_amount: 150,
|
refund_amount: 150,
|
||||||
..utils::PaymentRefundType::default().0
|
..utils::PaymentRefundType::default().0
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(),
|
get_default_payment_info(create_token().await),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response.response.unwrap_err().message,
|
response
|
||||||
"Refund amount (₹1.50) is greater than charge amount (₹1.00)",
|
.response
|
||||||
|
.unwrap_err()
|
||||||
|
.reason
|
||||||
|
.unwrap_or("".to_string()),
|
||||||
|
"The requested refund amount exceeds the amount available to refund.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,6 +69,8 @@ fn token_details() -> Option<types::PaymentMethodTokenizationData> {
|
|||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
browser_info: None,
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: enums::Currency::USD,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,6 +479,8 @@ async fn should_fail_payment_for_incorrect_cvc() {
|
|||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
browser_info: None,
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: enums::Currency::USD,
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(connector_customer_id, None),
|
get_default_payment_info(connector_customer_id, None),
|
||||||
)
|
)
|
||||||
@ -513,6 +517,8 @@ async fn should_fail_payment_for_invalid_exp_month() {
|
|||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
browser_info: None,
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: enums::Currency::USD,
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(connector_customer_id, None),
|
get_default_payment_info(connector_customer_id, None),
|
||||||
)
|
)
|
||||||
@ -549,6 +555,8 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
|
|||||||
..utils::CCardType::default().0
|
..utils::CCardType::default().0
|
||||||
}),
|
}),
|
||||||
browser_info: None,
|
browser_info: None,
|
||||||
|
amount: None,
|
||||||
|
currency: enums::Currency::USD,
|
||||||
}),
|
}),
|
||||||
get_default_payment_info(connector_customer_id, None),
|
get_default_payment_info(connector_customer_id, None),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -963,6 +963,8 @@ impl Default for TokenType {
|
|||||||
let data = types::PaymentMethodTokenizationData {
|
let data = types::PaymentMethodTokenizationData {
|
||||||
payment_method_data: types::api::PaymentMethodData::Card(CCardType::default().0),
|
payment_method_data: types::api::PaymentMethodData::Card(CCardType::default().0),
|
||||||
browser_info: None,
|
browser_info: None,
|
||||||
|
amount: Some(100),
|
||||||
|
currency: enums::Currency::USD,
|
||||||
};
|
};
|
||||||
Self(data)
|
Self(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ pub struct ConnectorAuthentication {
|
|||||||
pub powertranz: Option<BodyKey>,
|
pub powertranz: Option<BodyKey>,
|
||||||
pub rapyd: Option<BodyKey>,
|
pub rapyd: Option<BodyKey>,
|
||||||
pub shift4: Option<HeaderKey>,
|
pub shift4: Option<HeaderKey>,
|
||||||
pub square: Option<HeaderKey>,
|
pub square: Option<BodyKey>,
|
||||||
pub stax: Option<HeaderKey>,
|
pub stax: Option<HeaderKey>,
|
||||||
pub stripe: Option<HeaderKey>,
|
pub stripe: Option<HeaderKey>,
|
||||||
pub stripe_au: Option<HeaderKey>,
|
pub stripe_au: Option<HeaderKey>,
|
||||||
|
|||||||
@ -100,6 +100,7 @@ powertranz.base_url = "https://staging.ptranz.com/api/"
|
|||||||
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
rapyd.base_url = "https://sandboxapi.rapyd.net"
|
||||||
shift4.base_url = "https://api.shift4.com/"
|
shift4.base_url = "https://api.shift4.com/"
|
||||||
square.base_url = "https://connect.squareupsandbox.com/"
|
square.base_url = "https://connect.squareupsandbox.com/"
|
||||||
|
square.secondary_base_url = "https://pci-connect.squareupsandbox.com/"
|
||||||
stax.base_url = "https://apiprod.fattlabs.com/"
|
stax.base_url = "https://apiprod.fattlabs.com/"
|
||||||
stripe.base_url = "https://api.stripe.com/"
|
stripe.base_url = "https://api.stripe.com/"
|
||||||
stripe.base_url_file_upload = "https://files.stripe.com/"
|
stripe.base_url_file_upload = "https://files.stripe.com/"
|
||||||
|
|||||||
@ -3858,6 +3858,7 @@
|
|||||||
"powertranz",
|
"powertranz",
|
||||||
"rapyd",
|
"rapyd",
|
||||||
"shift4",
|
"shift4",
|
||||||
|
"square",
|
||||||
"stax",
|
"stax",
|
||||||
"stripe",
|
"stripe",
|
||||||
"trustpay",
|
"trustpay",
|
||||||
|
|||||||
Reference in New Issue
Block a user