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:
chikke srujan
2023-07-14 15:23:48 +05:30
committed by GitHub
parent 8832dd60b9
commit 05540ea17e
6 changed files with 386 additions and 74 deletions

43
Cargo.lock generated
View File

@ -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",

View File

@ -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)
}
}

View File

@ -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,
},
})
}
}

View File

@ -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 {

View File

@ -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,

View File

@ -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,