mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(connector): [Authorizedotnet] Add Wallet support (#1223)
Signed-off-by: chikke srujan <121822803+srujanchikke@users.noreply.github.com> Co-authored-by: Arjun Karthik <m.arjunkarthik@gmail.com>
This commit is contained in:
43
Cargo.lock
generated
43
Cargo.lock
generated
@ -1639,7 +1639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
@ -1856,6 +1856,12 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
@ -2301,7 +2307,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@ -2317,6 +2323,12 @@ dependencies = [
|
||||
"ahash 0.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@ -2555,10 +2567,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.2.3"
|
||||
@ -3215,7 +3237,7 @@ dependencies = [
|
||||
"fnv",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"thiserror",
|
||||
@ -3251,7 +3273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4007,6 +4029,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"darling 0.14.4",
|
||||
"diesel",
|
||||
"indexmap 2.0.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
@ -4279,7 +4302,7 @@ version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@ -4366,7 +4389,7 @@ dependencies = [
|
||||
"base64 0.21.2",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
@ -4975,7 +4998,7 @@ version = "0.19.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@ -5022,7 +5045,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"rand 0.8.5",
|
||||
@ -5279,7 +5302,7 @@ version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ae74ef183fae36d650f063ae7bde1cacbe1cd7e72b617cbe1e985551878b98"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"utoipa-gen",
|
||||
|
||||
@ -10,12 +10,15 @@ use transformers as authorizedotnet;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
consts,
|
||||
core::errors::{self, CustomResult},
|
||||
core::{
|
||||
errors::{self, CustomResult},
|
||||
payments,
|
||||
},
|
||||
headers,
|
||||
services::{self, request, ConnectorIntegration},
|
||||
types::{
|
||||
self,
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt, PaymentsCompleteAuthorize},
|
||||
},
|
||||
utils::{self, BytesExt},
|
||||
};
|
||||
@ -192,7 +195,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
@ -545,7 +548,7 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
@ -600,6 +603,100 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse
|
||||
let response: authorizedotnet::AuthorizedotnetSyncResponse = intermediate_response
|
||||
.parse_struct("AuthorizedotnetSyncResponse")
|
||||
.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: types::Response,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
get_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl PaymentsCompleteAuthorize for Authorizedotnet {}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::CompleteAuthorize,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> for Authorizedotnet
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
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::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(self.base_url(connectors).to_string())
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let connector_req = authorizedotnet::PaypalConfirmRequest::try_from(req)?;
|
||||
let authorizedotnet_req = types::RequestBody::log_and_get_request_body(
|
||||
&connector_req,
|
||||
utils::Encode::<authorizedotnet::PaypalConfirmRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(authorizedotnet_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsCompleteAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsCompleteAuthorizeType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsCompleteAuthorizeType::get_request_body(
|
||||
self, req,
|
||||
)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
|
||||
use bytes::Buf;
|
||||
|
||||
// Handle the case where response bytes contains U+FEFF (BOM) character sent by connector
|
||||
let encoding = encoding_rs::UTF_8;
|
||||
let intermediate_response = encoding.decode_with_bom_removal(res.response.chunk());
|
||||
let intermediate_response =
|
||||
bytes::Bytes::copy_from_slice(intermediate_response.0.as_bytes());
|
||||
|
||||
let response: authorizedotnet::AuthorizedotnetPaymentsResponse = intermediate_response
|
||||
.parse_struct("AuthorizedotnetPaymentsResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
@ -754,3 +851,14 @@ fn get_error_response(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl services::ConnectorRedirectResponse for Authorizedotnet {
|
||||
fn get_flow_type(
|
||||
&self,
|
||||
_query_params: &str,
|
||||
_json_payload: Option<serde_json::Value>,
|
||||
_action: services::PaymentAction,
|
||||
) -> CustomResult<payments::CallConnectorAction, errors::ConnectorError> {
|
||||
Ok(payments::CallConnectorAction::Trigger)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
use common_utils::ext_traits::{Encode, ValueExt};
|
||||
use error_stack::ResultExt;
|
||||
use common_utils::{
|
||||
errors::CustomResult,
|
||||
ext_traits::{Encode, ValueExt},
|
||||
};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::{PeekInterface, Secret, StrongSecret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::{CardData, PaymentsSyncRequestData, RefundsRequestData},
|
||||
connector::utils::{CardData, PaymentsSyncRequestData, RefundsRequestData, WalletData},
|
||||
core::errors,
|
||||
services,
|
||||
types::{self, api, storage::enums},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum TransactionType {
|
||||
#[serde(rename = "authCaptureTransaction")]
|
||||
Payment,
|
||||
@ -21,8 +26,12 @@ pub enum TransactionType {
|
||||
Refund,
|
||||
#[serde(rename = "voidTransaction")]
|
||||
Void,
|
||||
#[serde(rename = "authOnlyContinueTransaction")]
|
||||
ContinueAuthorization,
|
||||
#[serde(rename = "authCaptureContinueTransaction")]
|
||||
ContinueCapture,
|
||||
}
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct MerchantAuthentication {
|
||||
name: String,
|
||||
@ -44,33 +53,49 @@ impl TryFrom<&types::ConnectorAuthType> for MerchantAuthentication {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CreditCardDetails {
|
||||
card_number: masking::StrongSecret<String, cards::CardNumberStrategy>,
|
||||
expiration_date: masking::Secret<String>,
|
||||
card_number: StrongSecret<String, cards::CardNumberStrategy>,
|
||||
expiration_date: Secret<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
card_code: Option<masking::Secret<String>>,
|
||||
card_code: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BankAccountDetails {
|
||||
account_number: masking::Secret<String>,
|
||||
account_number: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum PaymentDetails {
|
||||
#[serde(rename = "creditCard")]
|
||||
CreditCard(CreditCardDetails),
|
||||
#[serde(rename = "bankAccount")]
|
||||
BankAccount(BankAccountDetails),
|
||||
Wallet,
|
||||
Klarna,
|
||||
Paypal,
|
||||
#[serde(rename = "bankRedirect")]
|
||||
BankRedirect,
|
||||
BankTransfer,
|
||||
OpaqueData(WalletDetails),
|
||||
PayPal(PayPalDetails),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PayPalDetails {
|
||||
pub success_url: Option<String>,
|
||||
pub cancel_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WalletDetails {
|
||||
pub data_descriptor: WalletMethod,
|
||||
pub data_value: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Deserialize)]
|
||||
pub enum WalletMethod {
|
||||
#[serde(rename = "COMMON.GOOGLE.INAPP.PAYMENT")]
|
||||
Googlepay,
|
||||
#[serde(rename = "COMMON.APPLE.INAPP.PAYMENT")]
|
||||
Applepay,
|
||||
}
|
||||
|
||||
fn get_pm_and_subsequent_auth_detail(
|
||||
@ -131,17 +156,12 @@ fn get_pm_and_subsequent_auth_detail(
|
||||
None,
|
||||
))
|
||||
}
|
||||
api::PaymentMethodData::PayLater(_) => Ok((PaymentDetails::Klarna, None, None)),
|
||||
api::PaymentMethodData::Wallet(_) => Ok((PaymentDetails::Wallet, None, None)),
|
||||
api::PaymentMethodData::BankRedirect(_) => {
|
||||
Ok((PaymentDetails::BankRedirect, None, None))
|
||||
}
|
||||
api::PaymentMethodData::Crypto(_)
|
||||
| api::PaymentMethodData::BankDebit(_)
|
||||
| api::PaymentMethodData::MandatePayment
|
||||
| api::PaymentMethodData::BankTransfer(_)
|
||||
| api::PaymentMethodData::Reward(_)
|
||||
| api::PaymentMethodData::Upi(_) => Err(errors::ConnectorError::NotSupported {
|
||||
api::PaymentMethodData::Wallet(ref wallet_data) => Ok((
|
||||
get_wallet_data(wallet_data, &item.request.complete_authorize_url)?,
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
_ => Err(errors::ConnectorError::NotSupported {
|
||||
message: format!("{:?}", item.request.payment_method_data),
|
||||
connector: "AuthorizeDotNet",
|
||||
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),
|
||||
@ -150,7 +170,7 @@ fn get_pm_and_subsequent_auth_detail(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct TransactionRequest {
|
||||
transaction_type: TransactionType,
|
||||
@ -163,13 +183,13 @@ struct TransactionRequest {
|
||||
authorization_indicator_type: Option<AuthorizationIndicator>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProcessingOptions {
|
||||
is_subsequent_auth: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SubsequentAuthInformation {
|
||||
original_network_trans_id: String,
|
||||
@ -177,7 +197,7 @@ pub struct SubsequentAuthInformation {
|
||||
reason: Reason,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Reason {
|
||||
Resubmission,
|
||||
@ -188,13 +208,13 @@ pub enum Reason {
|
||||
NoShow,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct AuthorizationIndicator {
|
||||
authorization_indicator: AuthorizationType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct TransactionVoidOrCaptureRequest {
|
||||
transaction_type: TransactionType,
|
||||
@ -202,34 +222,34 @@ struct TransactionVoidOrCaptureRequest {
|
||||
ref_trans_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizedotnetPaymentsRequest {
|
||||
merchant_authentication: MerchantAuthentication,
|
||||
transaction_request: TransactionRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizedotnetPaymentCancelOrCaptureRequest {
|
||||
merchant_authentication: MerchantAuthentication,
|
||||
transaction_request: TransactionVoidOrCaptureRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
// The connector enforces field ordering, it expects fields to be in the same order as in their API documentation
|
||||
pub struct CreateTransactionRequest {
|
||||
create_transaction_request: AuthorizedotnetPaymentsRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CancelOrCaptureTransactionRequest {
|
||||
create_transaction_request: AuthorizedotnetPaymentCancelOrCaptureRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AuthorizationType {
|
||||
Final,
|
||||
@ -315,7 +335,7 @@ impl TryFrom<&types::PaymentsCaptureRouterData> for CancelOrCaptureTransactionRe
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Default, serde::Deserialize)]
|
||||
pub enum AuthorizedotnetPaymentStatus {
|
||||
#[serde(rename = "1")]
|
||||
Approved,
|
||||
@ -326,9 +346,21 @@ pub enum AuthorizedotnetPaymentStatus {
|
||||
#[serde(rename = "4")]
|
||||
#[default]
|
||||
HeldForReview,
|
||||
#[serde(rename = "5")]
|
||||
RequiresAction,
|
||||
}
|
||||
|
||||
pub type AuthorizedotnetRefundStatus = AuthorizedotnetPaymentStatus;
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
pub enum AuthorizedotnetRefundStatus {
|
||||
#[serde(rename = "1")]
|
||||
Approved,
|
||||
#[serde(rename = "2")]
|
||||
Declined,
|
||||
#[serde(rename = "3")]
|
||||
Error,
|
||||
#[serde(rename = "4")]
|
||||
HeldForReview,
|
||||
}
|
||||
|
||||
impl From<AuthorizedotnetPaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: AuthorizedotnetPaymentStatus) -> Self {
|
||||
@ -337,6 +369,7 @@ impl From<AuthorizedotnetPaymentStatus> for enums::AttemptStatus {
|
||||
AuthorizedotnetPaymentStatus::Declined | AuthorizedotnetPaymentStatus::Error => {
|
||||
Self::Failure
|
||||
}
|
||||
AuthorizedotnetPaymentStatus::RequiresAction => Self::AuthenticationPending,
|
||||
AuthorizedotnetPaymentStatus::HeldForReview => Self::Pending,
|
||||
}
|
||||
}
|
||||
@ -362,26 +395,43 @@ pub struct ResponseMessages {
|
||||
pub message: Vec<ResponseMessage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ErrorMessage {
|
||||
pub error_code: String,
|
||||
pub error_text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionResponse {
|
||||
response_code: AuthorizedotnetPaymentStatus,
|
||||
auth_code: String,
|
||||
#[serde(rename = "transId")]
|
||||
transaction_id: String,
|
||||
network_trans_id: Option<String>,
|
||||
pub(super) account_number: Option<String>,
|
||||
pub(super) errors: Option<Vec<ErrorMessage>>,
|
||||
secure_acceptance: Option<SecureAcceptance>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RefundResponse {
|
||||
response_code: AuthorizedotnetRefundStatus,
|
||||
#[serde(rename = "transId")]
|
||||
transaction_id: String,
|
||||
network_trans_id: Option<String>,
|
||||
pub account_number: Option<String>,
|
||||
pub errors: Option<Vec<ErrorMessage>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct SecureAcceptance {
|
||||
secure_acceptance_url: Option<url::Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizedotnetPaymentsResponse {
|
||||
pub transaction_response: Option<TransactionResponse>,
|
||||
@ -471,6 +521,12 @@ impl<F, T>
|
||||
.change_context(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "connector_metadata",
|
||||
})?;
|
||||
let url = transaction_response
|
||||
.secure_acceptance
|
||||
.as_ref()
|
||||
.and_then(|x| x.secure_acceptance_url.to_owned());
|
||||
let redirection_data =
|
||||
url.map(|url| services::RedirectForm::from((url, services::Method::Get)));
|
||||
Ok(Self {
|
||||
status,
|
||||
response: match error {
|
||||
@ -479,7 +535,7 @@ impl<F, T>
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
transaction_response.transaction_id.clone(),
|
||||
),
|
||||
redirection_data: None,
|
||||
redirection_data,
|
||||
mandate_reference: None,
|
||||
connector_metadata: metadata,
|
||||
network_txn_id: transaction_response.network_trans_id.clone(),
|
||||
@ -623,14 +679,14 @@ impl<F> TryFrom<&types::RefundsRouterData<F>> for CreateRefundRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AuthorizedotnetPaymentStatus> for enums::RefundStatus {
|
||||
impl From<AuthorizedotnetRefundStatus> for enums::RefundStatus {
|
||||
fn from(item: AuthorizedotnetRefundStatus) -> Self {
|
||||
match item {
|
||||
AuthorizedotnetPaymentStatus::Approved => Self::Success,
|
||||
AuthorizedotnetPaymentStatus::Declined | AuthorizedotnetPaymentStatus::Error => {
|
||||
AuthorizedotnetRefundStatus::Approved => Self::Success,
|
||||
AuthorizedotnetRefundStatus::Declined | AuthorizedotnetRefundStatus::Error => {
|
||||
Self::Failure
|
||||
}
|
||||
AuthorizedotnetPaymentStatus::HeldForReview => Self::Pending,
|
||||
AuthorizedotnetRefundStatus::HeldForReview => Self::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -638,7 +694,7 @@ impl From<AuthorizedotnetPaymentStatus> for enums::RefundStatus {
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizedotnetRefundResponse {
|
||||
pub transaction_response: TransactionResponse,
|
||||
pub transaction_response: RefundResponse,
|
||||
pub messages: ResponseMessages,
|
||||
}
|
||||
|
||||
@ -847,7 +903,7 @@ impl<F, Req>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct ErrorDetails {
|
||||
pub code: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
@ -856,7 +912,7 @@ pub struct ErrorDetails {
|
||||
pub param: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
pub struct AuthorizedotnetErrorResponse {
|
||||
pub error: ErrorDetails,
|
||||
}
|
||||
@ -992,3 +1048,115 @@ impl TryFrom<AuthorizedotnetWebhookObjectId> for AuthorizedotnetSyncResponse {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_wallet_data(
|
||||
wallet_data: &api_models::payments::WalletData,
|
||||
return_url: &Option<String>,
|
||||
) -> CustomResult<PaymentDetails, errors::ConnectorError> {
|
||||
match wallet_data {
|
||||
api_models::payments::WalletData::GooglePay(_) => {
|
||||
Ok(PaymentDetails::OpaqueData(WalletDetails {
|
||||
data_descriptor: WalletMethod::Googlepay,
|
||||
data_value: wallet_data.get_encoded_wallet_token()?,
|
||||
}))
|
||||
}
|
||||
api_models::payments::WalletData::ApplePay(applepay_token) => {
|
||||
Ok(PaymentDetails::OpaqueData(WalletDetails {
|
||||
data_descriptor: WalletMethod::Applepay,
|
||||
data_value: applepay_token.payment_data.clone(),
|
||||
}))
|
||||
}
|
||||
api_models::payments::WalletData::PaypalRedirect(_) => {
|
||||
Ok(PaymentDetails::PayPal(PayPalDetails {
|
||||
success_url: return_url.to_owned(),
|
||||
cancel_url: return_url.to_owned(),
|
||||
}))
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment method".to_string(),
|
||||
))?,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizedotnetQueryParams {
|
||||
payer_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PaypalConfirmRequest {
|
||||
create_transaction_request: PaypalConfirmTransactionRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PaypalConfirmTransactionRequest {
|
||||
merchant_authentication: MerchantAuthentication,
|
||||
transaction_request: TransactionConfirmRequest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransactionConfirmRequest {
|
||||
transaction_type: TransactionType,
|
||||
payment: PaypalPaymentConfirm,
|
||||
ref_trans_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PaypalPaymentConfirm {
|
||||
pay_pal: Paypal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Paypal {
|
||||
#[serde(rename = "payerID")]
|
||||
payer_id: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PaypalQueryParams {
|
||||
#[serde(rename = "PayerID")]
|
||||
payer_id: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for PaypalConfirmRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
let params = item
|
||||
.request
|
||||
.redirect_response
|
||||
.as_ref()
|
||||
.and_then(|redirect_response| redirect_response.params.as_ref())
|
||||
.ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
let payer_id: Secret<String> = Secret::new(
|
||||
serde_urlencoded::from_str::<PaypalQueryParams>(params.peek())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?
|
||||
.payer_id,
|
||||
);
|
||||
let transaction_type = match item.request.capture_method {
|
||||
Some(enums::CaptureMethod::Manual) => TransactionType::ContinueAuthorization,
|
||||
_ => TransactionType::ContinueCapture,
|
||||
};
|
||||
let transaction_request = TransactionConfirmRequest {
|
||||
transaction_type,
|
||||
payment: PaypalPaymentConfirm {
|
||||
pay_pal: Paypal { payer_id },
|
||||
},
|
||||
ref_trans_id: item.request.connector_transaction_id.clone(),
|
||||
};
|
||||
|
||||
let merchant_authentication = MerchantAuthentication::try_from(&item.connector_auth_type)?;
|
||||
|
||||
Ok(Self {
|
||||
create_transaction_request: PaypalConfirmTransactionRequest {
|
||||
merchant_authentication,
|
||||
transaction_request,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -581,6 +581,7 @@ pub trait WalletData {
|
||||
fn get_wallet_token_as_json<T>(&self) -> Result<T, Error>
|
||||
where
|
||||
T: serde::de::DeserializeOwned;
|
||||
fn get_encoded_wallet_token(&self) -> Result<String, Error>;
|
||||
}
|
||||
|
||||
impl WalletData for api::WalletData {
|
||||
@ -600,6 +601,20 @@ impl WalletData for api::WalletData {
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::InvalidWalletToken)
|
||||
}
|
||||
|
||||
fn get_encoded_wallet_token(&self) -> Result<String, Error> {
|
||||
match self {
|
||||
Self::GooglePay(_) => {
|
||||
let json_token: serde_json::Value = self.get_wallet_token_as_json()?;
|
||||
let token_as_vec = serde_json::to_vec(&json_token)
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::InvalidWalletToken)?;
|
||||
let encoded_token = consts::BASE64_ENGINE.encode(token_as_vec);
|
||||
Ok(encoded_token)
|
||||
}
|
||||
_ => Err(errors::ConnectorError::InvalidWalletToken.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ApplePay {
|
||||
|
||||
@ -442,7 +442,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize {
|
||||
// If the status is terminal status, then redirect to merchant return url to provide status
|
||||
api_models::enums::IntentStatus::Succeeded
|
||||
| api_models::enums::IntentStatus::Failed
|
||||
| api_models::enums::IntentStatus::Cancelled | api_models::enums::IntentStatus::RequiresCapture=> helpers::get_handle_response_url(
|
||||
| api_models::enums::IntentStatus::Cancelled | api_models::enums::IntentStatus::RequiresCapture| api_models::enums::IntentStatus::Processing=> helpers::get_handle_response_url(
|
||||
payment_id,
|
||||
&merchant_account,
|
||||
payments_response,
|
||||
|
||||
@ -139,7 +139,6 @@ impl<const T: u8>
|
||||
default_imp_for_complete_authorize!(
|
||||
connector::Aci,
|
||||
connector::Adyen,
|
||||
connector::Authorizedotnet,
|
||||
connector::Bitpay,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
@ -273,7 +272,6 @@ impl<const T: u8> services::ConnectorRedirectResponse for connector::DummyConnec
|
||||
default_imp_for_connector_redirect_response!(
|
||||
connector::Aci,
|
||||
connector::Adyen,
|
||||
connector::Authorizedotnet,
|
||||
connector::Bitpay,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
|
||||
Reference in New Issue
Block a user