Skip to content

Commit

Permalink
feat(connector): [Paypal] Add payout flow for wallet(Paypal and Venmo) (
Browse files Browse the repository at this point in the history
#4406)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
Sakilmostak and hyperswitch-bot[bot] committed May 2, 2024
1 parent be44447 commit e4ed1e6
Show file tree
Hide file tree
Showing 20 changed files with 421 additions and 8 deletions.
16 changes: 16 additions & 0 deletions crates/api_models/src/payouts.rs
Expand Up @@ -289,13 +289,29 @@ pub struct PixBankTransfer {
#[serde(rename_all = "snake_case")]
pub enum Wallet {
Paypal(Paypal),
Venmo(Venmo),
}

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

/// mobile number linked to paypal account
#[schema(value_type = String, example = "16608213349")]
pub telephone_number: Option<Secret<String>>,

/// id of the paypal account
#[schema(value_type = String, example = "G83KXTJ5EHCQ2")]
pub paypal_id: Option<Secret<String>>,
}

#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
pub struct Venmo {
/// mobile number linked to venmo account
#[schema(value_type = String, example = "16608213349")]
pub telephone_number: Option<Secret<String>>,
}

#[derive(Debug, Default, ToSchema, Clone, Serialize)]
Expand Down
1 change: 1 addition & 0 deletions crates/common_enums/src/enums.rs
Expand Up @@ -1429,6 +1429,7 @@ pub enum PaymentMethodType {
Twint,
UpiCollect,
Vipps,
Venmo,
Walley,
WeChatPay,
SevenEleven,
Expand Down
1 change: 1 addition & 0 deletions crates/common_enums/src/transformers.rs
Expand Up @@ -1855,6 +1855,7 @@ impl From<PaymentMethodType> for PaymentMethod {
PaymentMethodType::Twint => Self::Wallet,
PaymentMethodType::UpiCollect => Self::Upi,
PaymentMethodType::Vipps => Self::Wallet,
PaymentMethodType::Venmo => Self::Wallet,
PaymentMethodType::Walley => Self::PayLater,
PaymentMethodType::WeChatPay => Self::Wallet,
PaymentMethodType::TouchNGo => Self::Wallet,
Expand Down
1 change: 1 addition & 0 deletions crates/euclid/src/frontend/dir/enums.rs
Expand Up @@ -89,6 +89,7 @@ pub enum WalletType {
TouchNGo,
Swish,
Cashapp,
Venmo,
}

#[derive(
Expand Down
1 change: 1 addition & 0 deletions crates/euclid/src/frontend/dir/lowering.rs
Expand Up @@ -56,6 +56,7 @@ impl From<enums::WalletType> for global_enums::PaymentMethodType {
enums::WalletType::TouchNGo => Self::TouchNGo,
enums::WalletType::Swish => Self::Swish,
enums::WalletType::Cashapp => Self::Cashapp,
enums::WalletType::Venmo => Self::Venmo,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/euclid/src/frontend/dir/transformers.rs
Expand Up @@ -167,6 +167,7 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet
global_enums::PaymentMethodType::CardRedirect => {
Ok(dirval!(CardRedirectType = CardRedirect))
}
global_enums::PaymentMethodType::Venmo => Ok(dirval!(WalletType = Venmo)),
}
}
}
1 change: 1 addition & 0 deletions crates/kgraph_utils/src/transformers.rs
Expand Up @@ -286,6 +286,7 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) {
api_enums::PaymentMethodType::CardRedirect => {
Ok(dirval!(CardRedirectType = CardRedirect))
}
api_enums::PaymentMethodType::Venmo => Ok(dirval!(WalletType = Venmo)),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/openapi/src/openapi.rs
Expand Up @@ -428,6 +428,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payouts::Card,
api_models::payouts::Wallet,
api_models::payouts::Paypal,
api_models::payouts::Venmo,
api_models::payouts::AchBankTransfer,
api_models::payouts::BacsBankTransfer,
api_models::payouts::SepaBankTransfer,
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/connector/adyen.rs
Expand Up @@ -106,6 +106,7 @@ impl ConnectorValidation for Adyen {
| PaymentMethodType::PayBright
| PaymentMethodType::Sepa
| PaymentMethodType::Vipps
| PaymentMethodType::Venmo
| PaymentMethodType::Paypal => match capture_method {
enums::CaptureMethod::Automatic
| enums::CaptureMethod::Manual
Expand Down
6 changes: 6 additions & 0 deletions crates/router/src/connector/adyen/transformers.rs
Expand Up @@ -4674,6 +4674,12 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutC
},
)?,
},
api_models::payouts::Wallet::Venmo(_) => {
Err(errors::ConnectorError::NotSupported {
message: "Venmo Wallet is not supported".to_string(),
connector: "Adyen",
})?
}
};
let address: &payments::AddressDetails = item.router_data.get_billing_address()?;
let payout_wallet = PayoutWalletData {
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/connector/klarna.rs
Expand Up @@ -398,6 +398,7 @@ impl
| common_enums::PaymentMethodType::Trustly
| common_enums::PaymentMethodType::Twint
| common_enums::PaymentMethodType::UpiCollect
| common_enums::PaymentMethodType::Venmo
| common_enums::PaymentMethodType::Vipps
| common_enums::PaymentMethodType::Walley
| common_enums::PaymentMethodType::WeChatPay
Expand Down
112 changes: 112 additions & 0 deletions crates/router/src/connector/paypal.rs
Expand Up @@ -6,6 +6,8 @@ use common_utils::{ext_traits::ByteSliceExt, request::RequestContent};
use diesel_models::enums;
use error_stack::ResultExt;
use masking::{ExposeInterface, PeekInterface, Secret};
#[cfg(feature = "payouts")]
use router_env::{instrument, tracing};
use transformers as paypal;

use self::transformers::{auth_headers, PaypalAuthResponse, PaypalMeta, PaypalWebhookEventType};
Expand Down Expand Up @@ -56,6 +58,12 @@ impl api::RefundExecute for Paypal {}
impl api::RefundSync for Paypal {}
impl api::ConnectorVerifyWebhookSource for Paypal {}

impl api::Payouts for Paypal {}
#[cfg(feature = "payouts")]
impl api::PayoutCreate for Paypal {}
#[cfg(feature = "payouts")]
impl api::PayoutFulfill for Paypal {}

impl Paypal {
pub fn get_order_error_response(
&self,
Expand Down Expand Up @@ -425,6 +433,110 @@ impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, t
}
}

#[cfg(feature = "payouts")]
impl ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::PayoutsResponseData>
for Paypal
{
fn get_url(
&self,
_req: &types::PayoutsRouterData<api::PoFulfill>,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!("{}v1/payments/payouts", self.base_url(connectors)))
}

fn get_headers(
&self,
req: &types::PayoutsRouterData<api::PoFulfill>,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}

fn get_request_body(
&self,
req: &types::PayoutsRouterData<api::PoFulfill>,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = paypal::PaypalRouterData::try_from((
&self.get_currency_unit(),
req.request.destination_currency,
req.request.amount,
req,
))?;
let connector_req = paypal::PaypalFulfillRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}

fn build_request(
&self,
req: &types::PayoutsRouterData<api::PoFulfill>,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
let request = services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PayoutFulfillType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::PayoutFulfillType::get_headers(
self, req, connectors,
)?)
.set_body(types::PayoutFulfillType::get_request_body(
self, req, connectors,
)?)
.build();

Ok(Some(request))
}

#[instrument(skip_all)]
fn handle_response(
&self,
data: &types::PayoutsRouterData<api::PoFulfill>,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<types::PayoutsRouterData<api::PoFulfill>, errors::ConnectorError> {
let response: paypal::PaypalFulfillResponse = res
.response
.parse_struct("PaypalFulfillResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);

types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}

fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
}

#[async_trait::async_trait]
#[cfg(feature = "payouts")]
impl ConnectorIntegration<api::PoCreate, types::PayoutsData, types::PayoutsResponseData>
for Paypal
{
fn build_request(
&self,
_req: &types::PayoutsRouterData<api::PoCreate>,
_connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
// Eligibility check for wallet is not implemented
Err(
errors::ConnectorError::NotImplemented("Payout Eligibility for Paypal".to_string())
.into(),
)
}
}

impl
ConnectorIntegration<
api::SetupMandate,
Expand Down

0 comments on commit e4ed1e6

Please sign in to comment.