Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/api_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ pub struct PaymentMethodCreate {
#[cfg(feature = "payouts")]
#[schema(value_type = Option<Bank>)]
pub bank_transfer: Option<payouts::Bank>,

/// Payment method details from locker
#[cfg(feature = "payouts")]
#[schema(value_type = Option<Wallet>)]
pub wallet: Option<payouts::Wallet>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
Expand All @@ -82,6 +87,11 @@ pub struct PaymentMethodUpdate {
#[schema(value_type = Option<Bank>)]
pub bank_transfer: Option<payouts::Bank>,

/// Payment method details from locker
#[cfg(feature = "payouts")]
#[schema(value_type = Option<Wallet>)]
pub wallet: Option<payouts::Wallet>,

/// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>,example = json!({ "city": "NY", "unit": "245" }))]
pub metadata: Option<pii::SecretSerdeValue>,
Expand Down
6 changes: 5 additions & 1 deletion crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1649,7 +1649,11 @@ pub struct WeChatPayQr {}
pub struct CashappQr {}

#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaypalRedirection {}
pub struct PaypalRedirection {
/// paypal's email address
#[schema(max_length = 255, value_type = Option<String>, example = "[email protected]")]
pub email: Option<Email>,
}

#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct AliPayQr {}
Expand Down
14 changes: 14 additions & 0 deletions crates/api_models/src/payouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ pub struct PayoutCreateRequest {
pub enum PayoutMethodData {
Card(Card),
Bank(Bank),
Wallet(Wallet),
}

impl Default for PayoutMethodData {
Expand Down Expand Up @@ -262,6 +263,19 @@ pub struct SepaBankTransfer {
pub bic: Option<Secret<String>>,
}

#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum Wallet {
Paypal(Paypal),
}

#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
pub struct Paypal {
/// Email linked with paypal account
#[schema(value_type = String, example = "[email protected]")]
pub email: Option<Email>,
}

#[derive(Debug, ToSchema, Clone, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PayoutCreateResponse {
Expand Down
2 changes: 2 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,7 @@ pub enum PayoutType {
#[default]
Card,
Bank,
Wallet,
}

#[derive(
Expand All @@ -2038,6 +2039,7 @@ pub enum PayoutEntityType {
Company,
NonProfit,
PublicSector,
NaturalPerson,

/// Wise
#[strum(serialize = "lowercase")]
Expand Down
2 changes: 2 additions & 0 deletions crates/connector_configs/toml/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2504,6 +2504,8 @@ api_key="Api Key"
payment_method_type = "bacs"
[[adyen_payout.bank_transfer]]
payment_method_type = "sepa"
[[adyen_payout.wallet]]
payment_method_type = "paypal"
[adyen_payout.connector_auth.SignatureKey]
api_key = "Adyen API Key (Payout creation)"
api_secret = "Adyen Key (Payout submission)"
Expand Down
2 changes: 2 additions & 0 deletions crates/connector_configs/toml/sandbox.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2506,6 +2506,8 @@ api_key="Api Key"
payment_method_type = "bacs"
[[adyen_payout.bank_transfer]]
payment_method_type = "sepa"
[[adyen_payout.wallet]]
payment_method_type = "paypal"
[adyen_payout.connector_auth.SignatureKey]
api_key = "Adyen API Key (Payout creation)"
api_secret = "Adyen Key (Payout submission)"
Expand Down
2 changes: 2 additions & 0 deletions crates/openapi/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payouts::PayoutCreateRequest,
api_models::payments::Address,
api_models::payouts::Card,
api_models::payouts::Wallet,
api_models::payouts::Paypal,
api_models::payouts::AchBankTransfer,
api_models::payouts::BacsBankTransfer,
api_models::payouts::SepaBankTransfer,
Expand Down
5 changes: 3 additions & 2 deletions crates/router/src/connector/adyen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,8 @@ impl services::ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::P
"{}pal/servlet/Payout/v68/{}",
connectors.adyen.secondary_base_url,
match req.request.payout_type {
storage_enums::PayoutType::Bank => "confirmThirdParty".to_string(),
storage_enums::PayoutType::Bank | storage_enums::PayoutType::Wallet =>
"confirmThirdParty".to_string(),
storage_enums::PayoutType::Card => "payout".to_string(),
}
))
Expand All @@ -1186,7 +1187,7 @@ impl services::ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::P
let mut api_key = vec![(
headers::X_API_KEY.to_string(),
match req.request.payout_type {
storage_enums::PayoutType::Bank => {
storage_enums::PayoutType::Bank | storage_enums::PayoutType::Wallet => {
auth.review_key.unwrap_or(auth.api_key).into_masked()
}
storage_enums::PayoutType::Card => auth.api_key.into_masked(),
Expand Down
123 changes: 106 additions & 17 deletions crates/router/src/connector/adyen/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3958,7 +3958,8 @@ pub struct AdyenPayoutCreateRequest {
amount: Amount,
recurring: RecurringContract,
merchant_account: Secret<String>,
bank: PayoutBankDetails,
#[serde(flatten)]
payment_data: PayoutPaymentMethodData,
reference: String,
shopper_reference: String,
shopper_email: Option<Email>,
Expand All @@ -3969,6 +3970,50 @@ pub struct AdyenPayoutCreateRequest {
billing_address: Option<Address>,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PayoutPaymentMethodData {
PayoutBankData(PayoutBankData),
PayoutWalletData(PayoutWalletData),
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PayoutBankData {
bank: PayoutBankDetails,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PayoutWalletData {
selected_brand: PayoutBrand,
additional_data: PayoutAdditionalData,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PayoutBrand {
Paypal,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct PayoutAdditionalData {
token_data_type: PayoutTokenDataType,
email_id: Email,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
enum PayoutTokenDataType {
PayPal,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -4054,14 +4099,14 @@ pub enum PayoutEligibility {
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AdyenPayoutFulfillRequest {
Bank(PayoutFulfillBankRequest),
GenericFulfillRequest(PayoutFulfillGenericRequest),
Card(Box<PayoutFulfillCardRequest>),
}

#[cfg(feature = "payouts")]
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PayoutFulfillBankRequest {
pub struct PayoutFulfillGenericRequest {
merchant_account: Secret<String>,
original_reference: String,
}
Expand Down Expand Up @@ -4199,6 +4244,7 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutC
connector: "Adyen",
})?,
};
let bank_data = PayoutBankData { bank: bank_details };
let address: &payments::AddressDetails = item.router_data.get_billing_address()?;
Ok(Self {
amount: Amount {
Expand All @@ -4209,13 +4255,52 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutC
contract: Contract::Payout,
},
merchant_account,
bank: bank_details,
payment_data: PayoutPaymentMethodData::PayoutBankData(bank_data),
reference: item.router_data.request.payout_id.to_owned(),
shopper_reference: item.router_data.merchant_id.to_owned(),
shopper_email: customer_email,
shopper_name: ShopperName {
first_name: address.get_first_name().ok().cloned(),
last_name: address.get_last_name().ok().cloned(),
first_name: Some(address.get_first_name()?.to_owned()), // it is a required field for payouts
last_name: Some(address.get_last_name()?.to_owned()), // it is a required field for payouts
},
date_of_birth: None,
entity_type: Some(item.router_data.request.entity_type),
nationality: get_country_code(item.router_data.address.billing.as_ref()),
billing_address: get_address_info(item.router_data.address.billing.as_ref()),
})
}
PayoutMethodData::Wallet(wallet_data) => {
let additional_data = match wallet_data {
api_models::payouts::Wallet::Paypal(paypal_data) => PayoutAdditionalData {
token_data_type: PayoutTokenDataType::PayPal,
email_id: paypal_data.email.clone().ok_or(
errors::ConnectorError::MissingRequiredField {
field_name: "email_address",
},
)?,
},
};
let address: &payments::AddressDetails = item.router_data.get_billing_address()?;
let payout_wallet = PayoutWalletData {
selected_brand: PayoutBrand::Paypal,
additional_data,
};
Ok(Self {
amount: Amount {
value: item.amount.to_owned(),
currency: item.router_data.request.destination_currency,
},
recurring: RecurringContract {
contract: Contract::Payout,
},
merchant_account,
payment_data: PayoutPaymentMethodData::PayoutWalletData(payout_wallet),
reference: item.router_data.request.payout_id.to_owned(),
shopper_reference: item.router_data.merchant_id.to_owned(),
shopper_email: customer_email,
shopper_name: ShopperName {
first_name: Some(address.get_first_name()?.to_owned()), // it is a required field for payouts
last_name: Some(address.get_last_name()?.to_owned()), // it is a required field for payouts
},
date_of_birth: None,
entity_type: Some(item.router_data.request.entity_type),
Expand All @@ -4236,15 +4321,19 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutF
let payout_type = item.router_data.request.payout_type.to_owned();
let merchant_account = auth_type.merchant_account;
match payout_type {
storage_enums::PayoutType::Bank => Ok(Self::Bank(PayoutFulfillBankRequest {
merchant_account,
original_reference: item
.router_data
.request
.connector_payout_id
.clone()
.unwrap_or("".to_string()),
})),
storage_enums::PayoutType::Bank | storage_enums::PayoutType::Wallet => {
Ok(Self::GenericFulfillRequest(PayoutFulfillGenericRequest {
merchant_account,
original_reference: item
.router_data
.request
.connector_payout_id
.clone()
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "connector_payout_id",
})?,
}))
}
storage_enums::PayoutType::Card => {
let address = item.router_data.get_billing_address()?;
Ok(Self::Card(Box::new(PayoutFulfillCardRequest {
Expand All @@ -4257,8 +4346,8 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutF
merchant_account,
reference: item.router_data.request.payout_id.clone(),
shopper_name: ShopperName {
first_name: address.get_first_name().ok().cloned(),
last_name: address.get_last_name().ok().cloned(),
first_name: Some(address.get_first_name()?.to_owned()), // it is a required field for payouts
last_name: Some(address.get_last_name()?.to_owned()), // it is a required field for payouts
},
nationality: get_country_code(item.router_data.address.billing.as_ref()),
entity_type: Some(item.router_data.request.entity_type),
Expand Down
35 changes: 22 additions & 13 deletions crates/router/src/connector/wise/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,11 @@ impl<F> TryFrom<&types::PayoutsRouterData<F>> for WiseRecipientCreateRequest {
}),
}?;
match request.payout_type.to_owned() {
storage_enums::PayoutType::Card => Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?,
storage_enums::PayoutType::Card | storage_enums::PayoutType::Wallet => {
Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?
}
storage_enums::PayoutType::Bank => {
let account_holder_name = customer_details
.ok_or(errors::ConnectorError::MissingRequiredField {
Expand Down Expand Up @@ -430,9 +432,11 @@ impl<F> TryFrom<&types::PayoutsRouterData<F>> for WisePayoutQuoteRequest {
target_currency: request.destination_currency.to_string(),
pay_out: WisePayOutOption::default(),
}),
storage_enums::PayoutType::Card => Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?,
storage_enums::PayoutType::Card | storage_enums::PayoutType::Wallet => {
Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?
}
}
}
}
Expand Down Expand Up @@ -486,9 +490,11 @@ impl<F> TryFrom<&types::PayoutsRouterData<F>> for WisePayoutCreateRequest {
details: wise_transfer_details,
})
}
storage_enums::PayoutType::Card => Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?,
storage_enums::PayoutType::Card | storage_enums::PayoutType::Wallet => {
Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?
}
}
}
}
Expand Down Expand Up @@ -529,9 +535,11 @@ impl<F> TryFrom<&types::PayoutsRouterData<F>> for WisePayoutFulfillRequest {
storage_enums::PayoutType::Bank => Ok(Self {
fund_type: FundType::default(),
}),
storage_enums::PayoutType::Card => Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?,
storage_enums::PayoutType::Card | storage_enums::PayoutType::Wallet => {
Err(errors::ConnectorError::NotImplemented(
utils::get_unimplemented_payment_method_error_message("Wise"),
))?
}
}
}
}
Expand Down Expand Up @@ -578,7 +586,8 @@ impl ForeignFrom<PayoutEntityType> for LegalType {
match entity_type {
PayoutEntityType::Individual
| PayoutEntityType::Personal
| PayoutEntityType::NonProfit => Self::Private,
| PayoutEntityType::NonProfit
| PayoutEntityType::NaturalPerson => Self::Private,
PayoutEntityType::Company
| PayoutEntityType::PublicSector
| PayoutEntityType::Business => Self::Business,
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/core/locker_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub async fn call_to_locker(
payment_method_issuer: pm.payment_method_issuer,
payment_method_issuer_code: pm.payment_method_issuer_code,
card: Some(card_details.clone()),
wallet: None,
bank_transfer: None,
metadata: pm.metadata,
customer_id: Some(pm.customer_id),
Expand Down
Loading