mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat: applepay through trustpay (#1422)
Co-authored-by: dracarys18 <karthikey.hegde@juspay.in>
This commit is contained in:
committed by
GitHub
parent
641995371d
commit
8032e0290b
@ -112,6 +112,9 @@ basilisk_host = "" # Basilisk host
|
||||
locker_setup = "legacy_locker" # With locker to use while in the deployed environment (eg. legacy_locker, basilisk_locker)
|
||||
locker_signing_key_id = "1" # Key_id to sign basilisk hs locker
|
||||
|
||||
[delayed_session_response]
|
||||
connectors_with_delayed_session_response = "trustpay" # List of connectors which has delayed session response
|
||||
|
||||
[jwekey] # 4 priv/pub key pair
|
||||
locker_key_identifier1 = "" # key identifier for key rotation , should be same as basilisk
|
||||
locker_key_identifier2 = "" # key identifier for key rotation , should be same as basilisk
|
||||
|
||||
@ -262,3 +262,6 @@ refund_duration = 1000
|
||||
refund_tolerance = 100
|
||||
refund_retrieve_duration = 500
|
||||
refund_retrieve_tolerance = 100
|
||||
|
||||
[delayed_session_response]
|
||||
connectors_with_delayed_session_response = "trustpay"
|
||||
|
||||
@ -148,6 +148,9 @@ cards = [
|
||||
"zen",
|
||||
]
|
||||
|
||||
[delayed_session_response]
|
||||
connectors_with_delayed_session_response = "trustpay"
|
||||
|
||||
|
||||
[scheduler]
|
||||
stream = "SCHEDULER_STREAM"
|
||||
|
||||
@ -829,6 +829,8 @@ pub enum WalletData {
|
||||
ApplePay(ApplePayWalletData),
|
||||
/// Wallet data for apple pay redirect flow
|
||||
ApplePayRedirect(Box<ApplePayRedirectData>),
|
||||
/// Wallet data for apple pay third party sdk flow
|
||||
ApplePayThirdPartySdk(Box<ApplePayThirdPartySdkData>),
|
||||
/// The wallet data for Google pay
|
||||
GooglePay(GooglePayWalletData),
|
||||
/// Wallet data for google pay redirect flow
|
||||
@ -864,6 +866,9 @@ pub struct ApplePayRedirectData {}
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
pub struct GooglePayRedirectData {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
pub struct ApplePayThirdPartySdkData {}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
pub struct WeChatPayRedirection {}
|
||||
|
||||
@ -1125,6 +1130,8 @@ pub enum NextActionData {
|
||||
DisplayBankTransferInformation {
|
||||
bank_transfer_steps_and_charges_details: BankTransferNextStepsData,
|
||||
},
|
||||
/// contains third party sdk session token response
|
||||
ThirdPartySdkSessionToken { session_token: Option<SessionToken> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
@ -1679,7 +1686,7 @@ pub struct PaymentsSessionRequest {
|
||||
pub merchant_connector_details: Option<admin::MerchantConnectorDetailsWrap>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct GpayAllowedMethodsParameters {
|
||||
/// The list of allowed auth methods (ex: 3DS, No3DS, PAN_ONLY etc)
|
||||
pub allowed_auth_methods: Vec<String>,
|
||||
@ -1687,7 +1694,7 @@ pub struct GpayAllowedMethodsParameters {
|
||||
pub allowed_card_networks: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct GpayTokenParameters {
|
||||
/// The name of the connector
|
||||
pub gateway: String,
|
||||
@ -1703,7 +1710,7 @@ pub struct GpayTokenParameters {
|
||||
pub stripe_publishable_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct GpayTokenizationSpecification {
|
||||
/// The token specification type(ex: PAYMENT_GATEWAY)
|
||||
#[serde(rename = "type")]
|
||||
@ -1712,7 +1719,7 @@ pub struct GpayTokenizationSpecification {
|
||||
pub parameters: GpayTokenParameters,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct GpayAllowedPaymentMethods {
|
||||
/// The type of payment method
|
||||
#[serde(rename = "type")]
|
||||
@ -1723,7 +1730,7 @@ pub struct GpayAllowedPaymentMethods {
|
||||
pub tokenization_specification: GpayTokenizationSpecification,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct GpayTransactionInfo {
|
||||
/// The country code
|
||||
#[schema(value_type = CountryAlpha2, example = "US")]
|
||||
@ -1736,7 +1743,7 @@ pub struct GpayTransactionInfo {
|
||||
pub total_price: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct GpayMerchantInfo {
|
||||
/// The name of the merchant
|
||||
pub merchant_name: String,
|
||||
@ -1802,7 +1809,7 @@ pub struct SessionTokenInfo {
|
||||
pub initiative_context: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
#[serde(tag = "wallet_name")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SessionToken {
|
||||
@ -1814,9 +1821,11 @@ pub enum SessionToken {
|
||||
Paypal(Box<PaypalSessionTokenResponse>),
|
||||
/// The session response structure for Apple Pay
|
||||
ApplePay(Box<ApplepaySessionTokenResponse>),
|
||||
/// Whenever there is no session token response or an error in session response
|
||||
NoSessionTokenReceived,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct GpaySessionTokenResponse {
|
||||
/// The merchant info
|
||||
@ -1828,7 +1837,7 @@ pub struct GpaySessionTokenResponse {
|
||||
pub connector: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct KlarnaSessionTokenResponse {
|
||||
/// The session token for Klarna
|
||||
@ -1837,26 +1846,58 @@ pub struct KlarnaSessionTokenResponse {
|
||||
pub session_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct PaypalSessionTokenResponse {
|
||||
/// The session token for PayPal
|
||||
pub session_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct ApplepaySessionTokenResponse {
|
||||
/// Session object for Apple Pay
|
||||
pub session_token_data: ApplePaySessionResponse,
|
||||
/// Payment request object for Apple Pay
|
||||
pub payment_request_data: ApplePayPaymentRequest,
|
||||
pub payment_request_data: Option<ApplePayPaymentRequest>,
|
||||
/// The session token is w.r.t this connector
|
||||
pub connector: String,
|
||||
/// Identifier for the delayed session response
|
||||
pub delayed_session_token: bool,
|
||||
/// The next action for the sdk (ex: calling confirm or sync call)
|
||||
pub sdk_next_action: SdkNextAction,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
#[derive(Debug, Eq, PartialEq, serde::Serialize, Clone, ToSchema)]
|
||||
pub struct SdkNextAction {
|
||||
/// The type of next action
|
||||
pub next_action: NextActionCall,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, serde::Serialize, Clone, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum NextActionCall {
|
||||
/// The next action call is confirm
|
||||
Confirm,
|
||||
/// The next action call is sync
|
||||
Sync,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
#[serde(untagged)]
|
||||
pub enum ApplePaySessionResponse {
|
||||
/// We get this session response, when third party sdk is involved
|
||||
ThirdPartySdk(ThirdPartySdkSessionResponse),
|
||||
/// We get this session response, when there is no involvement of third party sdk
|
||||
/// This is the common response most of the times
|
||||
NoThirdPartySdk(NoThirdPartySdkSessionResponse),
|
||||
/// This is for the empty session response
|
||||
NoSessionResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct ApplePaySessionResponse {
|
||||
pub struct NoThirdPartySdkSessionResponse {
|
||||
/// Timestamp at which session is requested
|
||||
pub epoch_timestamp: u64,
|
||||
/// Timestamp at which session expires
|
||||
@ -1881,7 +1922,22 @@ pub struct ApplePaySessionResponse {
|
||||
pub psp_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||
pub struct ThirdPartySdkSessionResponse {
|
||||
pub secrets: SecretInfoToInitiateSdk,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
pub struct SecretInfoToInitiateSdk {
|
||||
// Authorization secrets used by client to initiate sdk
|
||||
#[schema(value_type = String)]
|
||||
pub display: Secret<String>,
|
||||
// Authorization secrets used by client for payment
|
||||
#[schema(value_type = String)]
|
||||
pub payment: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
pub struct ApplePayPaymentRequest {
|
||||
/// The code for country
|
||||
#[schema(value_type = CountryAlpha2, example = "US")]
|
||||
@ -1894,16 +1950,16 @@ pub struct ApplePayPaymentRequest {
|
||||
pub merchant_capabilities: Vec<String>,
|
||||
/// The list of supported networks
|
||||
pub supported_networks: Vec<String>,
|
||||
pub merchant_identifier: String,
|
||||
pub merchant_identifier: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema, serde::Deserialize)]
|
||||
pub struct AmountInfo {
|
||||
/// The label must be the name of the merchant.
|
||||
pub label: String,
|
||||
/// A value that indicates whether the line item(Ex: total, tax, discount, or grand total) is final or pending.
|
||||
#[serde(rename = "type")]
|
||||
pub total_type: String,
|
||||
pub total_type: Option<String>,
|
||||
/// The total amount for the payment
|
||||
pub amount: String,
|
||||
}
|
||||
|
||||
@ -657,6 +657,9 @@ pub enum StripeNextAction {
|
||||
DisplayBankTransferInformation {
|
||||
bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData,
|
||||
},
|
||||
ThirdPartySdkSessionToken {
|
||||
session_token: Option<payments::SessionToken>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) fn into_stripe_next_action(
|
||||
@ -677,5 +680,8 @@ pub(crate) fn into_stripe_next_action(
|
||||
} => StripeNextAction::DisplayBankTransferInformation {
|
||||
bank_transfer_steps_and_charges_details,
|
||||
},
|
||||
payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
|
||||
StripeNextAction::ThirdPartySdkSessionToken { session_token }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -317,6 +317,9 @@ pub enum StripeNextAction {
|
||||
DisplayBankTransferInformation {
|
||||
bank_transfer_steps_and_charges_details: payments::BankTransferNextStepsData,
|
||||
},
|
||||
ThirdPartySdkSessionToken {
|
||||
session_token: Option<payments::SessionToken>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) fn into_stripe_next_action(
|
||||
@ -337,6 +340,9 @@ pub(crate) fn into_stripe_next_action(
|
||||
} => StripeNextAction::DisplayBankTransferInformation {
|
||||
bank_transfer_steps_and_charges_details,
|
||||
},
|
||||
payments::NextActionData::ThirdPartySdkSessionToken { session_token } => {
|
||||
StripeNextAction::ThirdPartySdkSessionToken { session_token }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -83,6 +83,7 @@ pub struct Settings {
|
||||
pub dummy_connector: DummyConnector,
|
||||
#[cfg(feature = "email")]
|
||||
pub email: EmailSettings,
|
||||
pub delayed_session_response: DelayedSessionConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
@ -507,6 +508,27 @@ pub struct FileUploadConfig {
|
||||
pub bucket_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct DelayedSessionConfig {
|
||||
#[serde(deserialize_with = "delayed_session_deser")]
|
||||
pub connectors_with_delayed_session_response: HashSet<api_models::enums::Connector>,
|
||||
}
|
||||
|
||||
fn delayed_session_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashSet<api_models::enums::Connector>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
value
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(api_models::enums::Connector::from_str)
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn new() -> ApplicationResult<Self> {
|
||||
Self::with_config_path(None)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use api_models::enums as api_enums;
|
||||
use api_models::{enums as api_enums, payments};
|
||||
use base64::Engine;
|
||||
use common_utils::{
|
||||
errors::CustomResult,
|
||||
@ -321,10 +321,11 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
|
||||
let wallet_token = consts::BASE64_ENGINE
|
||||
.decode(response.wallet_token.clone().expose())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
|
||||
|
||||
let session_response: api_models::payments::ApplePaySessionResponse = wallet_token[..]
|
||||
.parse_struct("ApplePayResponse")
|
||||
let session_response: api_models::payments::NoThirdPartySdkSessionResponse =
|
||||
wallet_token[..]
|
||||
.parse_struct("NoThirdPartySdkSessionResponse")
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
|
||||
let metadata = item.data.get_connector_meta()?.expose();
|
||||
@ -338,13 +339,16 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
|
||||
response: Ok(types::PaymentsResponseData::SessionResponse {
|
||||
session_token: types::api::SessionToken::ApplePay(Box::new(
|
||||
api_models::payments::ApplepaySessionTokenResponse {
|
||||
session_token_data: session_response,
|
||||
payment_request_data: api_models::payments::ApplePayPaymentRequest {
|
||||
session_token_data:
|
||||
api_models::payments::ApplePaySessionResponse::NoThirdPartySdk(
|
||||
session_response,
|
||||
),
|
||||
payment_request_data: Some(api_models::payments::ApplePayPaymentRequest {
|
||||
country_code: item.data.get_billing_country()?,
|
||||
currency_code: item.data.request.currency.to_string(),
|
||||
total: api_models::payments::AmountInfo {
|
||||
label: applepay_metadata.data.payment_request_data.label,
|
||||
total_type: "final".to_string(),
|
||||
total_type: Some("final".to_string()),
|
||||
amount: item.data.request.amount.to_string(),
|
||||
},
|
||||
merchant_capabilities: applepay_metadata
|
||||
@ -355,12 +359,20 @@ impl TryFrom<types::PaymentsSessionResponseRouterData<BluesnapWalletTokenRespons
|
||||
.data
|
||||
.payment_request_data
|
||||
.supported_networks,
|
||||
merchant_identifier: applepay_metadata
|
||||
merchant_identifier: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.session_token_data
|
||||
.merchant_identifier,
|
||||
},
|
||||
),
|
||||
}),
|
||||
connector: "bluesnap".to_string(),
|
||||
delayed_session_token: false,
|
||||
sdk_next_action: {
|
||||
payments::SdkNextAction {
|
||||
next_action: payments::NextActionCall::Confirm,
|
||||
}
|
||||
},
|
||||
},
|
||||
)),
|
||||
}),
|
||||
|
||||
@ -2167,8 +2167,11 @@ impl<F, T>
|
||||
};
|
||||
Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::PreProcessingResponse {
|
||||
pre_processing_id: item.response.id,
|
||||
pre_processing_id: types::PreprocessingResponseId::PreProcessingId(
|
||||
item.response.id,
|
||||
),
|
||||
connector_metadata: Some(connector_metadata),
|
||||
session_token: None,
|
||||
}),
|
||||
status,
|
||||
..item.data
|
||||
|
||||
@ -343,6 +343,102 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
|
||||
{
|
||||
}
|
||||
|
||||
impl api::PaymentsPreProcessing for Trustpay {}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::PreProcessing,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
> for Trustpay
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let mut header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsPreProcessingType::get_content_type(self)
|
||||
.to_string()
|
||||
.into(),
|
||||
)];
|
||||
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
|
||||
header.append(&mut api_key);
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!("{}{}", self.base_url(connectors), "api/v1/intent"))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
) -> CustomResult<Option<String>, errors::ConnectorError> {
|
||||
let create_intent_req = trustpay::TrustpayCreateIntentRequest::try_from(req)?;
|
||||
let trustpay_req =
|
||||
utils::Encode::<trustpay::TrustpayCreateIntentRequest>::url_encode(&create_intent_req)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(trustpay_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
let req = Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsPreProcessingType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.url(&types::PaymentsPreProcessingType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsPreProcessingType::get_request_body(
|
||||
self, req,
|
||||
)?)
|
||||
.build(),
|
||||
);
|
||||
Ok(req)
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsPreProcessingRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: trustpay::TrustpayCreateIntentResponse = res
|
||||
.response
|
||||
.parse_struct("TrustpayCreateIntentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl api::PaymentSession for Trustpay {}
|
||||
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
|
||||
@ -16,6 +16,7 @@ use crate::{
|
||||
core::errors,
|
||||
services,
|
||||
types::{self, api, storage::enums, BrowserInformation},
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
@ -474,7 +475,7 @@ pub struct PaymentsResponseCards {
|
||||
pub status: i64,
|
||||
pub description: Option<String>,
|
||||
pub instance_id: String,
|
||||
pub payment_status: String,
|
||||
pub payment_status: Option<String>,
|
||||
pub payment_description: Option<String>,
|
||||
pub redirect_url: Option<Url>,
|
||||
pub redirect_params: Option<HashMap<String, String>>,
|
||||
@ -553,8 +554,13 @@ fn handle_cards_response(
|
||||
),
|
||||
errors::ConnectorError,
|
||||
> {
|
||||
// By default, payment status is pending(000.200.000 status code)
|
||||
let (status, msg) = get_transaction_status(
|
||||
response.payment_status.as_str(),
|
||||
response
|
||||
.payment_status
|
||||
.to_owned()
|
||||
.unwrap_or("000.200.000".to_string())
|
||||
.as_str(),
|
||||
response.redirect_url.clone(),
|
||||
)?;
|
||||
let form_fields = response.redirect_params.unwrap_or_default();
|
||||
@ -567,7 +573,9 @@ fn handle_cards_response(
|
||||
});
|
||||
let error = if msg.is_some() {
|
||||
Some(types::ErrorResponse {
|
||||
code: response.payment_status,
|
||||
code: response
|
||||
.payment_status
|
||||
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
|
||||
message: msg.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: None,
|
||||
status_code,
|
||||
@ -802,6 +810,164 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, TrustpayAuthUpdateResponse, T, t
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TrustpayCreateIntentRequest {
|
||||
pub amount: String,
|
||||
pub currency: String,
|
||||
// If true, Apple Pay will be initialized
|
||||
pub init_apple_pay: Option<bool>,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsSessionRouterData> for TrustpayCreateIntentRequest {
|
||||
type Error = Error;
|
||||
fn try_from(item: &types::PaymentsSessionRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
amount: item.request.amount.to_string(),
|
||||
currency: item.request.currency.to_string(),
|
||||
init_apple_pay: Some(true),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsPreProcessingRouterData> for TrustpayCreateIntentRequest {
|
||||
type Error = Error;
|
||||
fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
amount: item
|
||||
.request
|
||||
.amount
|
||||
.get_required_value("amount")
|
||||
.change_context(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "amount",
|
||||
})?
|
||||
.to_string(),
|
||||
currency: item
|
||||
.request
|
||||
.currency
|
||||
.get_required_value("currency")
|
||||
.change_context(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "currency",
|
||||
})?
|
||||
.to_string(),
|
||||
init_apple_pay: Some(true),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TrustpayCreateIntentResponse {
|
||||
// TrustPay's authorization secrets used by client
|
||||
pub secrets: SdkSecretInfo,
|
||||
// Data object to be used for Apple Pay
|
||||
pub apple_init_result_data: TrustpayApplePayResponse,
|
||||
// Unique operation/transaction identifier
|
||||
pub instance_id: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SdkSecretInfo {
|
||||
pub display: Secret<String>,
|
||||
pub payment: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TrustpayApplePayResponse {
|
||||
pub country_code: api_models::enums::CountryAlpha2,
|
||||
pub currency_code: String,
|
||||
pub supported_networks: Vec<String>,
|
||||
pub merchant_capabilities: Vec<String>,
|
||||
pub total: ApplePayTotalInfo,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApplePayTotalInfo {
|
||||
pub label: String,
|
||||
pub amount: String,
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<F, TrustpayCreateIntentResponse, T, types::PaymentsResponseData>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = Error;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
TrustpayCreateIntentResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let response = item.response;
|
||||
|
||||
Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::PreProcessingResponse {
|
||||
connector_metadata: None,
|
||||
pre_processing_id: types::PreprocessingResponseId::ConnectorTransactionId(
|
||||
response.instance_id,
|
||||
),
|
||||
session_token: Some(types::api::SessionToken::ApplePay(Box::new(
|
||||
api_models::payments::ApplepaySessionTokenResponse {
|
||||
session_token_data:
|
||||
api_models::payments::ApplePaySessionResponse::ThirdPartySdk(
|
||||
api_models::payments::ThirdPartySdkSessionResponse {
|
||||
secrets: response.secrets.into(),
|
||||
},
|
||||
),
|
||||
payment_request_data: Some(api_models::payments::ApplePayPaymentRequest {
|
||||
country_code: response.apple_init_result_data.country_code,
|
||||
currency_code: response.apple_init_result_data.currency_code.clone(),
|
||||
supported_networks: response
|
||||
.apple_init_result_data
|
||||
.supported_networks
|
||||
.clone(),
|
||||
merchant_capabilities: response
|
||||
.apple_init_result_data
|
||||
.merchant_capabilities
|
||||
.clone(),
|
||||
total: response.apple_init_result_data.total.into(),
|
||||
merchant_identifier: None,
|
||||
}),
|
||||
connector: "trustpay".to_string(),
|
||||
delayed_session_token: true,
|
||||
sdk_next_action: {
|
||||
api_models::payments::SdkNextAction {
|
||||
next_action: api_models::payments::NextActionCall::Sync,
|
||||
}
|
||||
},
|
||||
},
|
||||
))),
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SdkSecretInfo> for api_models::payments::SecretInfoToInitiateSdk {
|
||||
fn from(value: SdkSecretInfo) -> Self {
|
||||
Self {
|
||||
display: value.display,
|
||||
payment: value.payment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApplePayTotalInfo> for api_models::payments::AmountInfo {
|
||||
fn from(value: ApplePayTotalInfo) -> Self {
|
||||
Self {
|
||||
label: value.label,
|
||||
amount: value.amount,
|
||||
total_type: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TrustpayRefundRequestCards {
|
||||
|
||||
@ -156,7 +156,7 @@ where
|
||||
&merchant_account,
|
||||
connector,
|
||||
&operation,
|
||||
&payment_data,
|
||||
&mut payment_data,
|
||||
&customer,
|
||||
call_connector_action,
|
||||
tokenization_action,
|
||||
@ -399,6 +399,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize {
|
||||
.and_then(|next_action_data| match next_action_data {
|
||||
api_models::payments::NextActionData::RedirectToUrl { redirect_to_url } => Some(redirect_to_url),
|
||||
api_models::payments::NextActionData::DisplayBankTransferInformation { .. } => None,
|
||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None
|
||||
})
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.into_report()
|
||||
@ -489,7 +490,7 @@ pub async fn call_connector_service<F, Op, Req>(
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
connector: api::ConnectorData,
|
||||
_operation: &Op,
|
||||
payment_data: &PaymentData<F>,
|
||||
payment_data: &mut PaymentData<F>,
|
||||
customer: &Option<domain::Customer>,
|
||||
call_connector_action: CallConnectorAction,
|
||||
tokenization_action: TokenizationAction,
|
||||
@ -542,6 +543,14 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Ok(types::PaymentsResponseData::PreProcessingResponse {
|
||||
session_token: Some(session_token),
|
||||
..
|
||||
}) = router_data.response.to_owned()
|
||||
{
|
||||
payment_data.sessions_token.push(session_token);
|
||||
};
|
||||
|
||||
let router_data_res = if should_continue_payment {
|
||||
router_data
|
||||
.decide_flows(
|
||||
@ -590,7 +599,6 @@ where
|
||||
|
||||
for session_connector_data in connectors.iter() {
|
||||
let connector_id = session_connector_data.connector.connector.id();
|
||||
|
||||
let router_data = payment_data
|
||||
.construct_router_data(state, connector_id, merchant_account, customer)
|
||||
.await?;
|
||||
@ -612,12 +620,19 @@ where
|
||||
let connector_name = session_connector.connector.connector_name.to_string();
|
||||
match connector_res {
|
||||
Ok(connector_response) => {
|
||||
if let Ok(types::PaymentsResponseData::SessionResponse { session_token }) =
|
||||
if let Ok(types::PaymentsResponseData::SessionResponse { session_token, .. }) =
|
||||
connector_response.response
|
||||
{
|
||||
// If session token is NoSessionTokenReceived, it is not pushed into the sessions_token as there is no response or there can be some error
|
||||
// In case of error, that error is already logged
|
||||
if !matches!(
|
||||
session_token,
|
||||
api_models::payments::SessionToken::NoSessionTokenReceived,
|
||||
) {
|
||||
payment_data.sessions_token.push(session_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(connector_error) => {
|
||||
logger::error!(
|
||||
"sessions_connector_error {} {:?}",
|
||||
@ -716,17 +731,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn complete_preprocessing_steps_if_required<F, Req, Res>(
|
||||
async fn complete_preprocessing_steps_if_required<F, Req>(
|
||||
state: &AppState,
|
||||
connector: &api::ConnectorData,
|
||||
payment_data: &PaymentData<F>,
|
||||
router_data: types::RouterData<F, Req, Res>,
|
||||
router_data: types::RouterData<F, Req, types::PaymentsResponseData>,
|
||||
should_continue_payment: bool,
|
||||
) -> RouterResult<(types::RouterData<F, Req, Res>, bool)>
|
||||
) -> RouterResult<(types::RouterData<F, Req, types::PaymentsResponseData>, bool)>
|
||||
where
|
||||
F: Send + Clone + Sync,
|
||||
Req: Send + Sync,
|
||||
types::RouterData<F, Req, Res>: Feature<F, Req> + Send,
|
||||
types::RouterData<F, Req, types::PaymentsResponseData>: Feature<F, Req> + Send,
|
||||
dyn api::Connector: services::api::ConnectorIntegration<F, Req, types::PaymentsResponseData>,
|
||||
{
|
||||
//TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check
|
||||
@ -744,6 +759,16 @@ where
|
||||
}
|
||||
_ => (router_data, should_continue_payment),
|
||||
},
|
||||
Some(api_models::payments::PaymentMethodData::Wallet(_)) => {
|
||||
if connector.connector_name.to_string() == *"trustpay" {
|
||||
(
|
||||
router_data.preprocessing_steps(state, connector).await?,
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
(router_data, should_continue_payment)
|
||||
}
|
||||
}
|
||||
_ => (router_data, should_continue_payment),
|
||||
};
|
||||
|
||||
@ -869,7 +894,6 @@ where
|
||||
.payment_method
|
||||
.get_required_value("payment_method")?;
|
||||
let payment_method_type = &payment_data.payment_attempt.payment_method_type;
|
||||
|
||||
let is_connector_tokenization_enabled =
|
||||
is_payment_method_tokenization_enabled_for_connector(
|
||||
state,
|
||||
|
||||
@ -647,7 +647,6 @@ default_imp_for_pre_processing_steps!(
|
||||
connector::Payu,
|
||||
connector::Rapyd,
|
||||
connector::Shift4,
|
||||
connector::Trustpay,
|
||||
connector::Worldline,
|
||||
connector::Worldpay,
|
||||
connector::Zen
|
||||
|
||||
@ -337,6 +337,7 @@ impl TryFrom<types::PaymentsAuthorizeData> for types::PaymentsPreProcessingData
|
||||
Ok(Self {
|
||||
email: data.email,
|
||||
currency: Some(data.currency),
|
||||
amount: Some(data.amount),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use api_models::payments as payment_types;
|
||||
use async_trait::async_trait;
|
||||
use common_utils::ext_traits::ByteSliceExt;
|
||||
use error_stack::{report, ResultExt};
|
||||
use error_stack::{Report, ResultExt};
|
||||
|
||||
use super::{ConstructFlowSpecificData, Feature};
|
||||
use crate::{
|
||||
@ -10,7 +10,7 @@ use crate::{
|
||||
errors::{self, ConnectorErrorExt, RouterResult},
|
||||
payments::{self, access_token, transformers, PaymentData},
|
||||
},
|
||||
headers,
|
||||
headers, logger,
|
||||
routes::{self, metrics},
|
||||
services,
|
||||
types::{self, api, domain},
|
||||
@ -78,18 +78,22 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_applepay_session_request(
|
||||
state: &routes::AppState,
|
||||
router_data: &types::PaymentsSessionRouterData,
|
||||
) -> RouterResult<(services::Request, payment_types::ApplepaySessionTokenData)> {
|
||||
let connector_metadata = router_data.connector_meta_data.clone();
|
||||
|
||||
let applepay_metadata = connector_metadata
|
||||
fn get_applepay_metadata(
|
||||
connector_metadata: Option<common_utils::pii::SecretSerdeValue>,
|
||||
) -> RouterResult<payment_types::ApplepaySessionTokenData> {
|
||||
connector_metadata
|
||||
.parse_value::<payment_types::ApplepaySessionTokenData>("ApplepaySessionTokenData")
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataFormat {
|
||||
field_name: "connector_metadata".to_string(),
|
||||
expected_format: "applepay_metadata_format".to_string(),
|
||||
})?;
|
||||
})
|
||||
}
|
||||
|
||||
fn mk_applepay_session_request(
|
||||
state: &routes::AppState,
|
||||
router_data: &types::PaymentsSessionRouterData,
|
||||
) -> RouterResult<services::Request> {
|
||||
let applepay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?;
|
||||
let request = payment_types::ApplepaySessionRequest {
|
||||
merchant_identifier: applepay_metadata
|
||||
.data
|
||||
@ -134,14 +138,10 @@ fn mk_applepay_session_request(
|
||||
.clone(),
|
||||
))
|
||||
.add_certificate_key(Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.session_token_data
|
||||
.certificate_keys
|
||||
.clone(),
|
||||
applepay_metadata.data.session_token_data.certificate_keys,
|
||||
))
|
||||
.build();
|
||||
Ok((session_request, applepay_metadata))
|
||||
Ok(session_request)
|
||||
}
|
||||
|
||||
async fn create_applepay_session_token(
|
||||
@ -149,42 +149,38 @@ async fn create_applepay_session_token(
|
||||
router_data: &types::PaymentsSessionRouterData,
|
||||
connector: &api::ConnectorData,
|
||||
) -> RouterResult<types::PaymentsSessionRouterData> {
|
||||
let (applepay_session_request, applepay_metadata) =
|
||||
mk_applepay_session_request(state, router_data)?;
|
||||
let response = services::call_connector_api(state, applepay_session_request)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failure in calling connector api")?;
|
||||
let session_response: payment_types::ApplePaySessionResponse = match response {
|
||||
Ok(resp) => resp
|
||||
.response
|
||||
.parse_struct("ApplePaySessionResponse")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to parse ApplePaySessionResponse struct"),
|
||||
Err(err) => {
|
||||
let error_response: payment_types::ApplepayErrorResponse = err
|
||||
.response
|
||||
.parse_struct("ApplepayErrorResponse")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to parse ApplepayErrorResponse struct")?;
|
||||
Err(
|
||||
report!(errors::ApiErrorResponse::InternalServerError).attach_printable(format!(
|
||||
"Failed with {} status code and the error response is {:?}",
|
||||
err.status_code, error_response
|
||||
)),
|
||||
let connectors_with_delayed_response = &state
|
||||
.conf
|
||||
.delayed_session_response
|
||||
.connectors_with_delayed_session_response;
|
||||
|
||||
let connector_name = connector.connector_name;
|
||||
let delayed_response = connectors_with_delayed_response.contains(&connector_name);
|
||||
|
||||
if delayed_response {
|
||||
let delayed_response_apple_pay_session =
|
||||
Some(payment_types::ApplePaySessionResponse::NoSessionResponse);
|
||||
create_apple_pay_session_response(
|
||||
router_data,
|
||||
delayed_response_apple_pay_session,
|
||||
None, // Apple pay payment request will be none for delayed session response
|
||||
connector_name.to_string(),
|
||||
delayed_response,
|
||||
payment_types::NextActionCall::Confirm,
|
||||
)
|
||||
}
|
||||
}?;
|
||||
} else {
|
||||
let applepay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?;
|
||||
|
||||
let amount_info = payment_types::AmountInfo {
|
||||
label: applepay_metadata.data.payment_request_data.label,
|
||||
total_type: "final".to_string(),
|
||||
total_type: Some("final".to_string()),
|
||||
amount: connector::utils::to_currency_base_unit(
|
||||
router_data.request.amount,
|
||||
router_data.request.currency,
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to convert currency to base unit")?,
|
||||
.change_context(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Failed to convert currency to base unit".to_string(),
|
||||
})?,
|
||||
};
|
||||
|
||||
let applepay_payment_request = payment_types::ApplePayPaymentRequest {
|
||||
@ -206,26 +202,85 @@ async fn create_applepay_session_token(
|
||||
.data
|
||||
.payment_request_data
|
||||
.supported_networks,
|
||||
merchant_identifier: applepay_metadata
|
||||
merchant_identifier: Some(
|
||||
applepay_metadata
|
||||
.data
|
||||
.session_token_data
|
||||
.merchant_identifier,
|
||||
),
|
||||
};
|
||||
|
||||
let response_router_data = types::PaymentsSessionRouterData {
|
||||
let applepay_session_request = mk_applepay_session_request(state, router_data)?;
|
||||
let response = services::call_connector_api(state, applepay_session_request).await;
|
||||
|
||||
// logging the error if present in session call response
|
||||
log_session_response_if_error(&response);
|
||||
|
||||
let apple_pay_session_response = response
|
||||
.ok()
|
||||
.and_then(|apple_pay_res| {
|
||||
apple_pay_res
|
||||
.map(|res| {
|
||||
let response: Result<
|
||||
payment_types::NoThirdPartySdkSessionResponse,
|
||||
Report<common_utils::errors::ParsingError>,
|
||||
> = res.response.parse_struct("NoThirdPartySdkSessionResponse");
|
||||
|
||||
// logging the parsing failed error
|
||||
if let Err(error) = response.as_ref() {
|
||||
logger::error!(?error);
|
||||
};
|
||||
|
||||
response.ok()
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let session_response =
|
||||
apple_pay_session_response.map(payment_types::ApplePaySessionResponse::NoThirdPartySdk);
|
||||
|
||||
create_apple_pay_session_response(
|
||||
router_data,
|
||||
session_response,
|
||||
Some(applepay_payment_request),
|
||||
connector_name.to_string(),
|
||||
delayed_response,
|
||||
payment_types::NextActionCall::Confirm,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_apple_pay_session_response(
|
||||
router_data: &types::PaymentsSessionRouterData,
|
||||
session_response: Option<payment_types::ApplePaySessionResponse>,
|
||||
apple_pay_payment_request: Option<payment_types::ApplePayPaymentRequest>,
|
||||
connector_name: String,
|
||||
delayed_response: bool,
|
||||
next_action: payment_types::NextActionCall,
|
||||
) -> RouterResult<types::PaymentsSessionRouterData> {
|
||||
match session_response {
|
||||
Some(response) => Ok(types::PaymentsSessionRouterData {
|
||||
response: Ok(types::PaymentsResponseData::SessionResponse {
|
||||
session_token: payment_types::SessionToken::ApplePay(Box::new(
|
||||
payment_types::ApplepaySessionTokenResponse {
|
||||
session_token_data: session_response,
|
||||
payment_request_data: applepay_payment_request,
|
||||
connector: connector.connector_name.to_string(),
|
||||
session_token_data: response,
|
||||
payment_request_data: apple_pay_payment_request,
|
||||
connector: connector_name,
|
||||
delayed_session_token: delayed_response,
|
||||
sdk_next_action: { payment_types::SdkNextAction { next_action } },
|
||||
},
|
||||
)),
|
||||
}),
|
||||
..router_data.clone()
|
||||
};
|
||||
|
||||
Ok(response_router_data)
|
||||
}),
|
||||
None => Ok(types::PaymentsSessionRouterData {
|
||||
response: Ok(types::PaymentsResponseData::SessionResponse {
|
||||
session_token: payment_types::SessionToken::NoSessionTokenReceived,
|
||||
}),
|
||||
..router_data.clone()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_gpay_session_token(
|
||||
@ -278,6 +333,18 @@ fn create_gpay_session_token(
|
||||
Ok(response_router_data)
|
||||
}
|
||||
|
||||
fn log_session_response_if_error(
|
||||
response: &Result<Result<types::Response, types::Response>, Report<errors::ApiClientError>>,
|
||||
) {
|
||||
if let Err(error) = response.as_ref() {
|
||||
logger::error!(?error);
|
||||
};
|
||||
response
|
||||
.as_ref()
|
||||
.ok()
|
||||
.map(|res| res.as_ref().map_err(|error| logger::error!(?error)));
|
||||
}
|
||||
|
||||
impl types::PaymentsSessionRouterData {
|
||||
pub async fn decide_flow<'a, 'b>(
|
||||
&'b self,
|
||||
|
||||
@ -315,13 +315,28 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
|
||||
types::PaymentsResponseData::PreProcessingResponse {
|
||||
pre_processing_id,
|
||||
connector_metadata,
|
||||
..
|
||||
} => {
|
||||
let connector_transaction_id = match pre_processing_id.to_owned() {
|
||||
types::PreprocessingResponseId::PreProcessingId(_) => None,
|
||||
types::PreprocessingResponseId::ConnectorTransactionId(connector_txn_id) => {
|
||||
Some(connector_txn_id)
|
||||
}
|
||||
};
|
||||
let preprocessing_step_id = match pre_processing_id {
|
||||
types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => {
|
||||
Some(pre_processing_id)
|
||||
}
|
||||
types::PreprocessingResponseId::ConnectorTransactionId(_) => None,
|
||||
};
|
||||
let payment_attempt_update = storage::PaymentAttemptUpdate::PreprocessingUpdate {
|
||||
status: router_data.status,
|
||||
payment_method_id: Some(router_data.payment_method_id),
|
||||
connector_metadata,
|
||||
preprocessing_step_id: Some(pre_processing_id),
|
||||
preprocessing_step_id,
|
||||
connector_transaction_id,
|
||||
};
|
||||
|
||||
(Some(payment_attempt_update), None)
|
||||
}
|
||||
types::PaymentsResponseData::TransactionResponse {
|
||||
|
||||
@ -377,15 +377,15 @@ where
|
||||
for (connector, payment_method_type, business_sub_label) in
|
||||
connector_and_supporting_payment_method_type
|
||||
{
|
||||
if let Ok(connector_data) = api::ConnectorData::get_connector_by_name(
|
||||
connectors,
|
||||
&connector,
|
||||
api::GetToken::from(payment_method_type),
|
||||
)
|
||||
let connector_type =
|
||||
get_connector_type_for_session_token(payment_method_type, request, &connector);
|
||||
if let Ok(connector_data) =
|
||||
api::ConnectorData::get_connector_by_name(connectors, &connector, connector_type)
|
||||
.map_err(|err| {
|
||||
logger::error!(session_token_error=?err);
|
||||
err
|
||||
}) {
|
||||
})
|
||||
{
|
||||
session_connector_data.push(api::SessionConnectorData {
|
||||
payment_method_type,
|
||||
connector: connector_data,
|
||||
@ -412,11 +412,11 @@ impl From<api_models::enums::PaymentMethodType> for api::GetToken {
|
||||
|
||||
pub fn get_connector_type_for_session_token(
|
||||
payment_method_type: api_models::enums::PaymentMethodType,
|
||||
_request: &api::PaymentsSessionRequest,
|
||||
connector: String,
|
||||
request: &api::PaymentsSessionRequest,
|
||||
connector: &str,
|
||||
) -> api::GetToken {
|
||||
if payment_method_type == api_models::enums::PaymentMethodType::ApplePay {
|
||||
if connector == *"bluesnap" {
|
||||
if is_apple_pay_get_token_connector(connector, request) {
|
||||
api::GetToken::Connector
|
||||
} else {
|
||||
api::GetToken::ApplePayMetadata
|
||||
@ -425,3 +425,11 @@ pub fn get_connector_type_for_session_token(
|
||||
api::GetToken::from(payment_method_type)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_apple_pay_get_token_connector(
|
||||
connector: &str,
|
||||
_request: &api::PaymentsSessionRequest,
|
||||
) -> bool {
|
||||
// Add connectors here, which all are required to hit connector for session call
|
||||
matches!(connector, "bluesnap")
|
||||
}
|
||||
|
||||
@ -169,6 +169,7 @@ where
|
||||
payment_data.connector_response.authentication_data,
|
||||
&operation,
|
||||
payment_data.ephemeral_key,
|
||||
payment_data.sessions_token,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -260,6 +261,7 @@ pub fn payments_to_payments_response<R, Op>(
|
||||
redirection_data: Option<serde_json::Value>,
|
||||
operation: &Op,
|
||||
ephemeral_key_option: Option<ephemeral_key::EphemeralKey>,
|
||||
session_tokens: Vec<api::SessionToken>,
|
||||
) -> RouterResponse<api::PaymentsResponse>
|
||||
where
|
||||
Op: Debug,
|
||||
@ -337,6 +339,15 @@ where
|
||||
}));
|
||||
};
|
||||
|
||||
// next action check for third party sdk session (for ex: Apple pay through trustpay has third party sdk session response)
|
||||
if third_party_sdk_session_next_action(&payment_attempt, operation) {
|
||||
next_action_response = Some(
|
||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken {
|
||||
session_token: session_tokens.get(0).cloned(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
let mut response: api::PaymentsResponse = Default::default();
|
||||
let routed_through = payment_attempt.connector.clone();
|
||||
|
||||
@ -514,6 +525,34 @@ where
|
||||
output
|
||||
}
|
||||
|
||||
pub fn third_party_sdk_session_next_action<Op>(
|
||||
payment_attempt: &storage::PaymentAttempt,
|
||||
operation: &Op,
|
||||
) -> bool
|
||||
where
|
||||
Op: Debug,
|
||||
{
|
||||
// If the operation is confirm, we will send session token response in next action
|
||||
if format!("{operation:?}").eq("PaymentConfirm") {
|
||||
payment_attempt
|
||||
.connector
|
||||
.as_ref()
|
||||
.map(|connector| matches!(connector.as_str(), "trustpay"))
|
||||
.and_then(|is_connector_supports_third_party_sdk| {
|
||||
if is_connector_supports_third_party_sdk {
|
||||
payment_attempt
|
||||
.payment_method
|
||||
.map(|pm| matches!(pm, storage_models::enums::PaymentMethod::Wallet))
|
||||
} else {
|
||||
Some(false)
|
||||
}
|
||||
})
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
||||
fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
|
||||
let pi = item.0;
|
||||
@ -900,6 +939,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsPreProce
|
||||
Ok(Self {
|
||||
email: payment_data.email,
|
||||
currency: Some(payment_data.currency),
|
||||
amount: Some(payment_data.amount.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,6 +201,9 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::PaymentsSessionResponse,
|
||||
api_models::payments::SessionToken,
|
||||
api_models::payments::ApplePaySessionResponse,
|
||||
api_models::payments::ThirdPartySdkSessionResponse,
|
||||
api_models::payments::NoThirdPartySdkSessionResponse,
|
||||
api_models::payments::SecretInfoToInitiateSdk,
|
||||
api_models::payments::ApplePayPaymentRequest,
|
||||
api_models::payments::AmountInfo,
|
||||
api_models::payments::GooglePayWalletData,
|
||||
@ -216,6 +219,8 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::KlarnaSessionTokenResponse,
|
||||
api_models::payments::PaypalSessionTokenResponse,
|
||||
api_models::payments::ApplepaySessionTokenResponse,
|
||||
api_models::payments::SdkNextAction,
|
||||
api_models::payments::NextActionCall,
|
||||
api_models::payments::GpayTokenizationData,
|
||||
api_models::payments::GooglePayPaymentMethodInfo,
|
||||
api_models::payments::ApplePayWalletData,
|
||||
@ -231,6 +236,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::ReceiverDetails,
|
||||
api_models::payments::AchTransfer,
|
||||
api_models::payments::ApplePayRedirectData,
|
||||
api_models::payments::ApplePayThirdPartySdkData,
|
||||
api_models::payments::GooglePayRedirectData,
|
||||
api_models::payments::SepaBankTransferInstructions,
|
||||
api_models::payments::BacsBankTransferInstructions,
|
||||
|
||||
@ -270,6 +270,7 @@ pub struct PaymentMethodTokenizationData {
|
||||
pub struct PaymentsPreProcessingData {
|
||||
pub email: Option<Email>,
|
||||
pub currency: Option<storage_enums::Currency>,
|
||||
pub amount: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -425,11 +426,18 @@ pub enum PaymentsResponseData {
|
||||
related_transaction_id: Option<String>,
|
||||
},
|
||||
PreProcessingResponse {
|
||||
pre_processing_id: String,
|
||||
pre_processing_id: PreprocessingResponseId,
|
||||
connector_metadata: Option<serde_json::Value>,
|
||||
session_token: Option<api::SessionToken>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PreprocessingResponseId {
|
||||
PreProcessingId(String),
|
||||
ConnectorTransactionId(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub enum ResponseId {
|
||||
ConnectorTransactionId(String),
|
||||
|
||||
@ -178,6 +178,7 @@ pub enum PaymentAttemptUpdate {
|
||||
payment_method_id: Option<Option<String>>,
|
||||
connector_metadata: Option<serde_json::Value>,
|
||||
preprocessing_step_id: Option<String>,
|
||||
connector_transaction_id: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -394,12 +395,14 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
|
||||
payment_method_id,
|
||||
connector_metadata,
|
||||
preprocessing_step_id,
|
||||
connector_transaction_id,
|
||||
} => Self {
|
||||
status: Some(status),
|
||||
payment_method_id,
|
||||
modified_at: Some(common_utils::date_time::now()),
|
||||
connector_metadata,
|
||||
preprocessing_step_id,
|
||||
connector_transaction_id,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user