Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(connector): [Paypal] Add payout flow for wallet(Paypal and Venmo) #4406

Merged
merged 25 commits into from May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9d11468
refactor: add configs for payout flows
Sakilmostak Apr 15, 2024
5ca3f1d
feat: skip payout create for paypal
Sakilmostak Apr 15, 2024
9e826af
feat: update status for paypal for payout_creation
Sakilmostak Apr 16, 2024
e0dfa4b
refactor: add configs for payout flows
Sakilmostak Apr 16, 2024
628bdfa
refactor: update condition for payout_object creation
Sakilmostak Apr 16, 2024
73e4ecf
feat: add access_token flow for payout
Sakilmostak Apr 16, 2024
7cc477b
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Apr 16, 2024
e1354a6
feat: add seperate access_token module for payouts
Sakilmostak Apr 18, 2024
31794f8
refactor: add feature flags for payout specific methods
Sakilmostak Apr 18, 2024
604297a
refactor: resolve comments
Sakilmostak Apr 18, 2024
c2aece2
chore: run formatter
hyperswitch-bot[bot] Apr 18, 2024
f65b2b5
feat: add payout flow for paypal
Sakilmostak Apr 21, 2024
29cd6ab
refactor: generate openapi spec
Sakilmostak Apr 21, 2024
2a04371
refactor: resolve ci checks
Sakilmostak Apr 21, 2024
0b4c90e
refactor: resolve ci checks
Sakilmostak Apr 21, 2024
d9af876
refactor: resolve ci checks
Sakilmostak Apr 22, 2024
6c5444f
Merge branch 'main' into paypal_payout
Sakilmostak Apr 25, 2024
baebc18
refactor: resolve openapi check
Sakilmostak Apr 25, 2024
a98a6e4
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Apr 25, 2024
5faa5ca
refactor: resolve comments
Sakilmostak Apr 29, 2024
9997600
refactor: merge main
Sakilmostak Apr 29, 2024
c2c44b0
refactor: update with main
Sakilmostak Apr 29, 2024
016ec72
Merge branch 'main' into paypal_payout
Sakilmostak Apr 29, 2024
cb64646
refactor: resolve comments
Sakilmostak Apr 30, 2024
fe17397
refactor: resolve ci checks
Sakilmostak May 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions crates/api_models/src/payouts.rs
Expand Up @@ -265,13 +265,29 @@ pub struct SepaBankTransfer {
#[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>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it include phone country code as well? eg: +91

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope


/// 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 @@ -4670,6 +4670,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