mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
refactor(connector): [Paypal] Add support for both BodyKey and SignatureKey (#2633)
Co-authored-by: Mani Chandra Dulam <mani.dchandra@juspay.in> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com>
This commit is contained in:
@ -5,10 +5,10 @@ use base64::Engine;
|
|||||||
use common_utils::ext_traits::ByteSliceExt;
|
use common_utils::ext_traits::ByteSliceExt;
|
||||||
use diesel_models::enums;
|
use diesel_models::enums;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::PeekInterface;
|
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||||
use transformers as paypal;
|
use transformers as paypal;
|
||||||
|
|
||||||
use self::transformers::{PaypalAuthResponse, PaypalMeta, PaypalWebhookEventType};
|
use self::transformers::{auth_headers, PaypalAuthResponse, PaypalMeta, PaypalWebhookEventType};
|
||||||
use super::utils::PaymentsCompleteAuthorizeRequestData;
|
use super::utils::PaymentsCompleteAuthorizeRequestData;
|
||||||
use crate::{
|
use crate::{
|
||||||
configs::settings,
|
configs::settings,
|
||||||
@ -31,7 +31,7 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
api::{self, CompleteAuthorize, ConnectorCommon, ConnectorCommonExt, VerifyWebhookSource},
|
api::{self, CompleteAuthorize, ConnectorCommon, ConnectorCommonExt, VerifyWebhookSource},
|
||||||
transformers::ForeignFrom,
|
transformers::ForeignFrom,
|
||||||
ErrorResponse, Response,
|
ConnectorAuthType, ErrorResponse, Response,
|
||||||
},
|
},
|
||||||
utils::{self, BytesExt},
|
utils::{self, BytesExt},
|
||||||
};
|
};
|
||||||
@ -110,8 +110,8 @@ where
|
|||||||
.clone()
|
.clone()
|
||||||
.ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
|
.ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
let key = &req.attempt_id;
|
let key = &req.attempt_id;
|
||||||
|
let auth = paypal::PaypalAuthType::try_from(&req.connector_auth_type)?;
|
||||||
Ok(vec![
|
let mut headers = vec![
|
||||||
(
|
(
|
||||||
headers::CONTENT_TYPE.to_string(),
|
headers::CONTENT_TYPE.to_string(),
|
||||||
self.get_content_type().to_string().into(),
|
self.get_content_type().to_string().into(),
|
||||||
@ -121,17 +121,57 @@ where
|
|||||||
format!("Bearer {}", access_token.token.peek()).into_masked(),
|
format!("Bearer {}", access_token.token.peek()).into_masked(),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Prefer".to_string(),
|
auth_headers::PREFER.to_string(),
|
||||||
"return=representation".to_string().into(),
|
"return=representation".to_string().into(),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"PayPal-Request-Id".to_string(),
|
auth_headers::PAYPAL_REQUEST_ID.to_string(),
|
||||||
key.to_string().into_masked(),
|
key.to_string().into_masked(),
|
||||||
),
|
),
|
||||||
])
|
];
|
||||||
|
if let Ok(paypal::PaypalConnectorCredentials::PartnerIntegration(credentials)) =
|
||||||
|
auth.get_credentials()
|
||||||
|
{
|
||||||
|
let auth_assertion_header =
|
||||||
|
construct_auth_assertion_header(&credentials.payer_id, &credentials.client_id);
|
||||||
|
headers.extend(vec![
|
||||||
|
(
|
||||||
|
auth_headers::PAYPAL_AUTH_ASSERTION.to_string(),
|
||||||
|
auth_assertion_header.to_string().into_masked(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
auth_headers::PAYPAL_PARTNER_ATTRIBUTION_ID.to_string(),
|
||||||
|
"HyperSwitchPPCP_SP".to_string().into(),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
headers.extend(vec![(
|
||||||
|
auth_headers::PAYPAL_PARTNER_ATTRIBUTION_ID.to_string(),
|
||||||
|
"HyperSwitchlegacy_Ecom".to_string().into(),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
Ok(headers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn construct_auth_assertion_header(
|
||||||
|
payer_id: &Secret<String>,
|
||||||
|
client_id: &Secret<String>,
|
||||||
|
) -> String {
|
||||||
|
let algorithm = consts::BASE64_ENGINE
|
||||||
|
.encode("{\"alg\":\"none\"}")
|
||||||
|
.to_string();
|
||||||
|
let merchant_credentials = format!(
|
||||||
|
"{{\"iss\":\"{}\",\"payer_id\":\"{}\"}}",
|
||||||
|
client_id.clone().expose(),
|
||||||
|
payer_id.clone().expose()
|
||||||
|
);
|
||||||
|
let encoded_credentials = consts::BASE64_ENGINE
|
||||||
|
.encode(merchant_credentials)
|
||||||
|
.to_string();
|
||||||
|
format!("{algorithm}.{encoded_credentials}.")
|
||||||
|
}
|
||||||
|
|
||||||
impl ConnectorCommon for Paypal {
|
impl ConnectorCommon for Paypal {
|
||||||
fn id(&self) -> &'static str {
|
fn id(&self) -> &'static str {
|
||||||
"paypal"
|
"paypal"
|
||||||
@ -151,14 +191,14 @@ impl ConnectorCommon for Paypal {
|
|||||||
|
|
||||||
fn get_auth_header(
|
fn get_auth_header(
|
||||||
&self,
|
&self,
|
||||||
auth_type: &types::ConnectorAuthType,
|
auth_type: &ConnectorAuthType,
|
||||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||||
let auth: paypal::PaypalAuthType = auth_type
|
let auth = paypal::PaypalAuthType::try_from(auth_type)?;
|
||||||
.try_into()
|
let credentials = auth.get_credentials()?;
|
||||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
|
||||||
Ok(vec![(
|
Ok(vec![(
|
||||||
headers::AUTHORIZATION.to_string(),
|
headers::AUTHORIZATION.to_string(),
|
||||||
auth.api_key.into_masked(),
|
credentials.get_client_secret().into_masked(),
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,15 +300,9 @@ impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, t
|
|||||||
req: &types::RefreshTokenRouterData,
|
req: &types::RefreshTokenRouterData,
|
||||||
_connectors: &settings::Connectors,
|
_connectors: &settings::Connectors,
|
||||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||||
let auth: paypal::PaypalAuthType = (&req.connector_auth_type)
|
let auth = paypal::PaypalAuthType::try_from(&req.connector_auth_type)?;
|
||||||
.try_into()
|
let credentials = auth.get_credentials()?;
|
||||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
let auth_val = credentials.generate_authorization_value();
|
||||||
|
|
||||||
let auth_id = auth
|
|
||||||
.key1
|
|
||||||
.zip(auth.api_key)
|
|
||||||
.map(|(key1, api_key)| format!("{}:{}", key1, api_key));
|
|
||||||
let auth_val = format!("Basic {}", consts::BASE64_ENGINE.encode(auth_id.peek()));
|
|
||||||
|
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(
|
(
|
||||||
@ -998,15 +1032,9 @@ impl
|
|||||||
>,
|
>,
|
||||||
_connectors: &settings::Connectors,
|
_connectors: &settings::Connectors,
|
||||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||||
let auth: paypal::PaypalAuthType = (&req.connector_auth_type)
|
let auth = paypal::PaypalAuthType::try_from(&req.connector_auth_type)?;
|
||||||
.try_into()
|
let credentials = auth.get_credentials()?;
|
||||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
let auth_val = credentials.generate_authorization_value();
|
||||||
|
|
||||||
let auth_id = auth
|
|
||||||
.key1
|
|
||||||
.zip(auth.api_key)
|
|
||||||
.map(|(key1, api_key)| format!("{}:{}", key1, api_key));
|
|
||||||
let auth_val = format!("Basic {}", consts::BASE64_ENGINE.encode(auth_id.peek()));
|
|
||||||
|
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
(
|
(
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use api_models::{enums, payments::BankRedirectData};
|
use api_models::{enums, payments::BankRedirectData};
|
||||||
|
use base64::Engine;
|
||||||
use common_utils::errors::CustomResult;
|
use common_utils::errors::CustomResult;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::Secret;
|
use masking::{ExposeInterface, Secret};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -11,10 +12,11 @@ use crate::{
|
|||||||
self, to_connector_meta, AccessTokenRequestInfo, AddressDetailsData,
|
self, to_connector_meta, AccessTokenRequestInfo, AddressDetailsData,
|
||||||
BankRedirectBillingData, CardData, PaymentsAuthorizeRequestData,
|
BankRedirectBillingData, CardData, PaymentsAuthorizeRequestData,
|
||||||
},
|
},
|
||||||
|
consts,
|
||||||
core::errors,
|
core::errors,
|
||||||
services,
|
services,
|
||||||
types::{
|
types::{
|
||||||
self, api, storage::enums as storage_enums, transformers::ForeignFrom,
|
self, api, storage::enums as storage_enums, transformers::ForeignFrom, ConnectorAuthType,
|
||||||
VerifyWebhookSourceResponseData,
|
VerifyWebhookSourceResponseData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -57,6 +59,12 @@ mod webhook_headers {
|
|||||||
pub const PAYPAL_CERT_URL: &str = "paypal-cert-url";
|
pub const PAYPAL_CERT_URL: &str = "paypal-cert-url";
|
||||||
pub const PAYPAL_AUTH_ALGO: &str = "paypal-auth-algo";
|
pub const PAYPAL_AUTH_ALGO: &str = "paypal-auth-algo";
|
||||||
}
|
}
|
||||||
|
pub mod auth_headers {
|
||||||
|
pub const PAYPAL_PARTNER_ATTRIBUTION_ID: &str = "PayPal-Partner-Attribution-Id";
|
||||||
|
pub const PREFER: &str = "Prefer";
|
||||||
|
pub const PAYPAL_REQUEST_ID: &str = "PayPal-Request-Id";
|
||||||
|
pub const PAYPAL_AUTH_ASSERTION: &str = "PayPal-Auth-Assertion";
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
@ -72,19 +80,111 @@ pub struct OrderAmount {
|
|||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct OrderRequestAmount {
|
||||||
|
pub currency_code: storage_enums::Currency,
|
||||||
|
pub value: String,
|
||||||
|
pub breakdown: AmountBreakdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for OrderRequestAmount {
|
||||||
|
fn from(item: &PaypalRouterData<&types::PaymentsAuthorizeRouterData>) -> Self {
|
||||||
|
Self {
|
||||||
|
currency_code: item.router_data.request.currency,
|
||||||
|
value: item.amount.to_owned(),
|
||||||
|
breakdown: AmountBreakdown {
|
||||||
|
item_total: OrderAmount {
|
||||||
|
currency_code: item.router_data.request.currency,
|
||||||
|
value: item.amount.to_owned(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct AmountBreakdown {
|
||||||
|
item_total: OrderAmount,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||||
pub struct PurchaseUnitRequest {
|
pub struct PurchaseUnitRequest {
|
||||||
reference_id: Option<String>, //reference for an item in purchase_units
|
reference_id: Option<String>, //reference for an item in purchase_units
|
||||||
invoice_id: Option<String>, //The API caller-provided external invoice number for this order. Appears in both the payer's transaction history and the emails that the payer receives.
|
invoice_id: Option<String>, //The API caller-provided external invoice number for this order. Appears in both the payer's transaction history and the emails that the payer receives.
|
||||||
custom_id: Option<String>, //Used to reconcile client transactions with PayPal transactions.
|
custom_id: Option<String>, //Used to reconcile client transactions with PayPal transactions.
|
||||||
amount: OrderAmount,
|
amount: OrderRequestAmount,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
payee: Option<Payee>,
|
||||||
|
shipping: Option<ShippingAddress>,
|
||||||
|
items: Vec<ItemDetails>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||||
|
pub struct Payee {
|
||||||
|
merchant_id: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||||
|
pub struct ItemDetails {
|
||||||
|
name: String,
|
||||||
|
quantity: u16,
|
||||||
|
unit_amount: OrderAmount,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for ItemDetails {
|
||||||
|
fn from(item: &PaypalRouterData<&types::PaymentsAuthorizeRouterData>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: format!(
|
||||||
|
"Payment for invoice {}",
|
||||||
|
item.router_data.connector_request_reference_id
|
||||||
|
),
|
||||||
|
quantity: 1,
|
||||||
|
unit_amount: OrderAmount {
|
||||||
|
currency_code: item.router_data.request.currency,
|
||||||
|
value: item.amount.to_string(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
address_line_1: Option<Secret<String>>,
|
address_line_1: Option<Secret<String>>,
|
||||||
postal_code: Option<Secret<String>>,
|
postal_code: Option<Secret<String>>,
|
||||||
country_code: api_models::enums::CountryAlpha2,
|
country_code: api_models::enums::CountryAlpha2,
|
||||||
|
admin_area_2: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||||
|
pub struct ShippingAddress {
|
||||||
|
address: Option<Address>,
|
||||||
|
name: Option<ShippingName>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for ShippingAddress {
|
||||||
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
|
|
||||||
|
fn try_from(
|
||||||
|
item: &PaypalRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
address: get_address_info(item.router_data.address.shipping.as_ref())?,
|
||||||
|
name: Some(ShippingName {
|
||||||
|
full_name: item
|
||||||
|
.router_data
|
||||||
|
.address
|
||||||
|
.shipping
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|inner_data| inner_data.address.as_ref())
|
||||||
|
.and_then(|inner_data| inner_data.first_name.clone()),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
|
||||||
|
pub struct ShippingName {
|
||||||
|
full_name: Option<Secret<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
@ -124,6 +224,22 @@ pub struct RedirectRequest {
|
|||||||
pub struct ContextStruct {
|
pub struct ContextStruct {
|
||||||
return_url: Option<String>,
|
return_url: Option<String>,
|
||||||
cancel_url: Option<String>,
|
cancel_url: Option<String>,
|
||||||
|
user_action: Option<UserAction>,
|
||||||
|
shipping_preference: ShippingPreference,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub enum UserAction {
|
||||||
|
#[serde(rename = "PAY_NOW")]
|
||||||
|
PayNow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub enum ShippingPreference {
|
||||||
|
#[serde(rename = "SET_PROVIDED_ADDRESS")]
|
||||||
|
SetProvidedAddress,
|
||||||
|
#[serde(rename = "GET_FROM_FILE")]
|
||||||
|
GetFromFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
@ -158,6 +274,7 @@ fn get_address_info(
|
|||||||
country_code: address.get_country()?.to_owned(),
|
country_code: address.get_country()?.to_owned(),
|
||||||
address_line_1: address.line1.clone(),
|
address_line_1: address.line1.clone(),
|
||||||
postal_code: address.zip.clone(),
|
postal_code: address.zip.clone(),
|
||||||
|
admin_area_2: address.city.clone(),
|
||||||
}),
|
}),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
@ -180,6 +297,12 @@ fn get_payment_source(
|
|||||||
experience_context: ContextStruct {
|
experience_context: ContextStruct {
|
||||||
return_url: item.request.complete_authorize_url.clone(),
|
return_url: item.request.complete_authorize_url.clone(),
|
||||||
cancel_url: item.request.complete_authorize_url.clone(),
|
cancel_url: item.request.complete_authorize_url.clone(),
|
||||||
|
shipping_preference: if item.address.shipping.is_some() {
|
||||||
|
ShippingPreference::SetProvidedAddress
|
||||||
|
} else {
|
||||||
|
ShippingPreference::GetFromFile
|
||||||
|
},
|
||||||
|
user_action: Some(UserAction::PayNow),
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
BankRedirectData::Giropay {
|
BankRedirectData::Giropay {
|
||||||
@ -194,6 +317,12 @@ fn get_payment_source(
|
|||||||
experience_context: ContextStruct {
|
experience_context: ContextStruct {
|
||||||
return_url: item.request.complete_authorize_url.clone(),
|
return_url: item.request.complete_authorize_url.clone(),
|
||||||
cancel_url: item.request.complete_authorize_url.clone(),
|
cancel_url: item.request.complete_authorize_url.clone(),
|
||||||
|
shipping_preference: if item.address.shipping.is_some() {
|
||||||
|
ShippingPreference::SetProvidedAddress
|
||||||
|
} else {
|
||||||
|
ShippingPreference::GetFromFile
|
||||||
|
},
|
||||||
|
user_action: Some(UserAction::PayNow),
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
BankRedirectData::Ideal {
|
BankRedirectData::Ideal {
|
||||||
@ -208,6 +337,12 @@ fn get_payment_source(
|
|||||||
experience_context: ContextStruct {
|
experience_context: ContextStruct {
|
||||||
return_url: item.request.complete_authorize_url.clone(),
|
return_url: item.request.complete_authorize_url.clone(),
|
||||||
cancel_url: item.request.complete_authorize_url.clone(),
|
cancel_url: item.request.complete_authorize_url.clone(),
|
||||||
|
shipping_preference: if item.address.shipping.is_some() {
|
||||||
|
ShippingPreference::SetProvidedAddress
|
||||||
|
} else {
|
||||||
|
ShippingPreference::GetFromFile
|
||||||
|
},
|
||||||
|
user_action: Some(UserAction::PayNow),
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
BankRedirectData::Sofort {
|
BankRedirectData::Sofort {
|
||||||
@ -220,6 +355,12 @@ fn get_payment_source(
|
|||||||
experience_context: ContextStruct {
|
experience_context: ContextStruct {
|
||||||
return_url: item.request.complete_authorize_url.clone(),
|
return_url: item.request.complete_authorize_url.clone(),
|
||||||
cancel_url: item.request.complete_authorize_url.clone(),
|
cancel_url: item.request.complete_authorize_url.clone(),
|
||||||
|
shipping_preference: if item.address.shipping.is_some() {
|
||||||
|
ShippingPreference::SetProvidedAddress
|
||||||
|
} else {
|
||||||
|
ShippingPreference::GetFromFile
|
||||||
|
},
|
||||||
|
user_action: Some(UserAction::PayNow),
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
BankRedirectData::BancontactCard { .. }
|
BankRedirectData::BancontactCard { .. }
|
||||||
@ -247,11 +388,24 @@ fn get_payment_source(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_payee(auth_type: &PaypalAuthType) -> Option<Payee> {
|
||||||
|
auth_type
|
||||||
|
.get_credentials()
|
||||||
|
.ok()
|
||||||
|
.and_then(|credentials| credentials.get_payer_id())
|
||||||
|
.map(|payer_id| Payee {
|
||||||
|
merchant_id: payer_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalPaymentsRequest {
|
impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalPaymentsRequest {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(
|
fn try_from(
|
||||||
item: &PaypalRouterData<&types::PaymentsAuthorizeRouterData>,
|
item: &PaypalRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
|
let paypal_auth: PaypalAuthType =
|
||||||
|
PaypalAuthType::try_from(&item.router_data.connector_auth_type)?;
|
||||||
|
let payee = get_payee(&paypal_auth);
|
||||||
match item.router_data.request.payment_method_data {
|
match item.router_data.request.payment_method_data {
|
||||||
api_models::payments::PaymentMethodData::Card(ref ccard) => {
|
api_models::payments::PaymentMethodData::Card(ref ccard) => {
|
||||||
let intent = if item.router_data.request.is_auto_capture()? {
|
let intent = if item.router_data.request.is_auto_capture()? {
|
||||||
@ -259,18 +413,20 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP
|
|||||||
} else {
|
} else {
|
||||||
PaypalPaymentIntent::Authorize
|
PaypalPaymentIntent::Authorize
|
||||||
};
|
};
|
||||||
let amount = OrderAmount {
|
let amount = OrderRequestAmount::from(item);
|
||||||
currency_code: item.router_data.request.currency,
|
|
||||||
value: item.amount.to_owned(),
|
|
||||||
};
|
|
||||||
let connector_request_reference_id =
|
let connector_request_reference_id =
|
||||||
item.router_data.connector_request_reference_id.clone();
|
item.router_data.connector_request_reference_id.clone();
|
||||||
|
let shipping_address = ShippingAddress::try_from(item)?;
|
||||||
|
let item_details = vec![ItemDetails::from(item)];
|
||||||
|
|
||||||
let purchase_units = vec![PurchaseUnitRequest {
|
let purchase_units = vec![PurchaseUnitRequest {
|
||||||
reference_id: Some(connector_request_reference_id.clone()),
|
reference_id: Some(connector_request_reference_id.clone()),
|
||||||
custom_id: Some(connector_request_reference_id.clone()),
|
custom_id: Some(connector_request_reference_id.clone()),
|
||||||
invoice_id: Some(connector_request_reference_id),
|
invoice_id: Some(connector_request_reference_id),
|
||||||
amount,
|
amount,
|
||||||
|
payee,
|
||||||
|
shipping: Some(shipping_address),
|
||||||
|
items: item_details,
|
||||||
}];
|
}];
|
||||||
let card = item.router_data.request.get_card()?;
|
let card = item.router_data.request.get_card()?;
|
||||||
let expiry = Some(card.get_expiry_date_as_yyyymm("-"));
|
let expiry = Some(card.get_expiry_date_as_yyyymm("-"));
|
||||||
@ -306,25 +462,29 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP
|
|||||||
} else {
|
} else {
|
||||||
PaypalPaymentIntent::Authorize
|
PaypalPaymentIntent::Authorize
|
||||||
};
|
};
|
||||||
let amount = OrderAmount {
|
let amount = OrderRequestAmount::from(item);
|
||||||
currency_code: item.router_data.request.currency,
|
|
||||||
value: item.amount.to_owned(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let connector_req_reference_id =
|
let connector_req_reference_id =
|
||||||
item.router_data.connector_request_reference_id.clone();
|
item.router_data.connector_request_reference_id.clone();
|
||||||
|
let shipping_address = ShippingAddress::try_from(item)?;
|
||||||
|
let item_details = vec![ItemDetails::from(item)];
|
||||||
|
|
||||||
let purchase_units = vec![PurchaseUnitRequest {
|
let purchase_units = vec![PurchaseUnitRequest {
|
||||||
reference_id: Some(connector_req_reference_id.clone()),
|
reference_id: Some(connector_req_reference_id.clone()),
|
||||||
custom_id: Some(connector_req_reference_id.clone()),
|
custom_id: Some(connector_req_reference_id.clone()),
|
||||||
invoice_id: Some(connector_req_reference_id),
|
invoice_id: Some(connector_req_reference_id),
|
||||||
amount,
|
amount,
|
||||||
|
payee,
|
||||||
|
shipping: Some(shipping_address),
|
||||||
|
items: item_details,
|
||||||
}];
|
}];
|
||||||
let payment_source =
|
let payment_source =
|
||||||
Some(PaymentSourceItem::Paypal(PaypalRedirectionRequest {
|
Some(PaymentSourceItem::Paypal(PaypalRedirectionRequest {
|
||||||
experience_context: ContextStruct {
|
experience_context: ContextStruct {
|
||||||
return_url: item.router_data.request.complete_authorize_url.clone(),
|
return_url: item.router_data.request.complete_authorize_url.clone(),
|
||||||
cancel_url: item.router_data.request.complete_authorize_url.clone(),
|
cancel_url: item.router_data.request.complete_authorize_url.clone(),
|
||||||
|
shipping_preference: ShippingPreference::SetProvidedAddress,
|
||||||
|
user_action: Some(UserAction::PayNow),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -374,18 +534,20 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP
|
|||||||
connector: "Paypal".to_string(),
|
connector: "Paypal".to_string(),
|
||||||
})?
|
})?
|
||||||
};
|
};
|
||||||
let amount = OrderAmount {
|
let amount = OrderRequestAmount::from(item);
|
||||||
currency_code: item.router_data.request.currency,
|
|
||||||
value: item.amount.to_owned(),
|
|
||||||
};
|
|
||||||
let connector_req_reference_id =
|
let connector_req_reference_id =
|
||||||
item.router_data.connector_request_reference_id.clone();
|
item.router_data.connector_request_reference_id.clone();
|
||||||
|
let shipping_address = ShippingAddress::try_from(item)?;
|
||||||
|
let item_details = vec![ItemDetails::from(item)];
|
||||||
|
|
||||||
let purchase_units = vec![PurchaseUnitRequest {
|
let purchase_units = vec![PurchaseUnitRequest {
|
||||||
reference_id: Some(connector_req_reference_id.clone()),
|
reference_id: Some(connector_req_reference_id.clone()),
|
||||||
custom_id: Some(connector_req_reference_id.clone()),
|
custom_id: Some(connector_req_reference_id.clone()),
|
||||||
invoice_id: Some(connector_req_reference_id),
|
invoice_id: Some(connector_req_reference_id),
|
||||||
amount,
|
amount,
|
||||||
|
payee,
|
||||||
|
shipping: Some(shipping_address),
|
||||||
|
items: item_details,
|
||||||
}];
|
}];
|
||||||
let payment_source =
|
let payment_source =
|
||||||
Some(get_payment_source(item.router_data, bank_redirection_data)?);
|
Some(get_payment_source(item.router_data, bank_redirection_data)?);
|
||||||
@ -604,19 +766,98 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, PaypalAuthUpdateResponse, T, typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PaypalAuthType {
|
pub enum PaypalAuthType {
|
||||||
pub(super) api_key: Secret<String>,
|
TemporaryAuth,
|
||||||
pub(super) key1: Secret<String>,
|
AuthWithDetails(PaypalConnectorCredentials),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&types::ConnectorAuthType> for PaypalAuthType {
|
#[derive(Debug)]
|
||||||
|
pub enum PaypalConnectorCredentials {
|
||||||
|
StandardIntegration(StandardFlowCredentials),
|
||||||
|
PartnerIntegration(PartnerFlowCredentials),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaypalConnectorCredentials {
|
||||||
|
pub fn get_client_id(&self) -> Secret<String> {
|
||||||
|
match self {
|
||||||
|
Self::StandardIntegration(item) => item.client_id.clone(),
|
||||||
|
Self::PartnerIntegration(item) => item.client_id.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_client_secret(&self) -> Secret<String> {
|
||||||
|
match self {
|
||||||
|
Self::StandardIntegration(item) => item.client_secret.clone(),
|
||||||
|
Self::PartnerIntegration(item) => item.client_secret.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_payer_id(&self) -> Option<Secret<String>> {
|
||||||
|
match self {
|
||||||
|
Self::StandardIntegration(_) => None,
|
||||||
|
Self::PartnerIntegration(item) => Some(item.payer_id.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_authorization_value(&self) -> String {
|
||||||
|
let auth_id = format!(
|
||||||
|
"{}:{}",
|
||||||
|
self.get_client_id().expose(),
|
||||||
|
self.get_client_secret().expose(),
|
||||||
|
);
|
||||||
|
format!("Basic {}", consts::BASE64_ENGINE.encode(auth_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StandardFlowCredentials {
|
||||||
|
pub(super) client_id: Secret<String>,
|
||||||
|
pub(super) client_secret: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PartnerFlowCredentials {
|
||||||
|
pub(super) client_id: Secret<String>,
|
||||||
|
pub(super) client_secret: Secret<String>,
|
||||||
|
pub(super) payer_id: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaypalAuthType {
|
||||||
|
pub fn get_credentials(
|
||||||
|
&self,
|
||||||
|
) -> CustomResult<&PaypalConnectorCredentials, errors::ConnectorError> {
|
||||||
|
match self {
|
||||||
|
Self::TemporaryAuth => Err(errors::ConnectorError::InvalidConnectorConfig {
|
||||||
|
config: "TemporaryAuth found in connector_account_details",
|
||||||
|
}
|
||||||
|
.into()),
|
||||||
|
Self::AuthWithDetails(credentials) => Ok(credentials),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&ConnectorAuthType> for PaypalAuthType {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||||
match auth_type {
|
match auth_type {
|
||||||
types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
|
types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self::AuthWithDetails(
|
||||||
api_key: api_key.to_owned(),
|
PaypalConnectorCredentials::StandardIntegration(StandardFlowCredentials {
|
||||||
key1: key1.to_owned(),
|
client_id: key1.to_owned(),
|
||||||
}),
|
client_secret: api_key.to_owned(),
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
types::ConnectorAuthType::SignatureKey {
|
||||||
|
api_key,
|
||||||
|
key1,
|
||||||
|
api_secret,
|
||||||
|
} => Ok(Self::AuthWithDetails(
|
||||||
|
PaypalConnectorCredentials::PartnerIntegration(PartnerFlowCredentials {
|
||||||
|
client_id: key1.to_owned(),
|
||||||
|
client_secret: api_key.to_owned(),
|
||||||
|
payer_id: api_secret.to_owned(),
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
types::ConnectorAuthType::TemporaryAuth => Ok(Self::TemporaryAuth),
|
||||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType)?,
|
_ => Err(errors::ConnectorError::FailedToObtainAuthType)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user