feat(connector): [Adyen] implement Gopay for Adyen (#1557)

Co-authored-by: Sangamesh Kulkarni <59434228+Sangamesh26@users.noreply.github.com>
This commit is contained in:
AkshayaFoiger
2023-07-17 18:07:32 +05:30
committed by GitHub
parent 94a5eb3533
commit de2d9bd059
10 changed files with 113 additions and 14 deletions

View File

@ -952,6 +952,8 @@ pub enum WalletData {
AliPayRedirect(AliPayRedirection), AliPayRedirect(AliPayRedirection),
/// The wallet data for Ali Pay HK redirect /// The wallet data for Ali Pay HK redirect
AliPayHkRedirect(AliPayHkRedirection), AliPayHkRedirect(AliPayHkRedirection),
/// The wallet data for GoPay redirect
GoPayRedirect(GoPayRedirection),
/// The wallet data for Apple pay /// The wallet data for Apple pay
ApplePay(ApplePayWalletData), ApplePay(ApplePayWalletData),
/// Wallet data for apple pay redirect flow /// Wallet data for apple pay redirect flow
@ -1036,6 +1038,9 @@ pub struct AliPayRedirection {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct AliPayHkRedirection {} pub struct AliPayHkRedirection {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct GoPayRedirection {}
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct MobilePayRedirection {} pub struct MobilePayRedirection {}

View File

@ -570,6 +570,7 @@ pub enum PaymentMethodType {
Evoucher, Evoucher,
Giropay, Giropay,
GooglePay, GooglePay,
GoPay,
Ideal, Ideal,
Interac, Interac,
Klarna, Klarna,

View File

@ -1554,6 +1554,7 @@ impl From<PaymentMethodType> for PaymentMethod {
PaymentMethodType::Evoucher => Self::Reward, PaymentMethodType::Evoucher => Self::Reward,
PaymentMethodType::Giropay => Self::BankRedirect, PaymentMethodType::Giropay => Self::BankRedirect,
PaymentMethodType::GooglePay => Self::Wallet, PaymentMethodType::GooglePay => Self::Wallet,
PaymentMethodType::GoPay => Self::Wallet,
PaymentMethodType::Ideal => Self::BankRedirect, PaymentMethodType::Ideal => Self::BankRedirect,
PaymentMethodType::Klarna => Self::PayLater, PaymentMethodType::Klarna => Self::PayLater,
PaymentMethodType::MbWay => Self::Wallet, PaymentMethodType::MbWay => Self::Wallet,

View File

@ -907,7 +907,7 @@ impl api::IncomingWebhook for Adyen {
let notif = get_webhook_object_from_body(request.body) let notif = get_webhook_object_from_body(request.body)
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
let response: adyen::AdyenResponse = notif.into(); let response: adyen::Response = notif.into();
let res_json = serde_json::to_value(response) let res_json = serde_json::to_value(response)
.into_report() .into_report()

View File

@ -116,6 +116,7 @@ pub struct AdyenPaymentRequest<'a> {
delivery_address: Option<Address>, delivery_address: Option<Address>,
country_code: Option<api_enums::CountryAlpha2>, country_code: Option<api_enums::CountryAlpha2>,
line_items: Option<Vec<LineItem>>, line_items: Option<Vec<LineItem>>,
channel: Option<Channel>,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -145,6 +146,11 @@ pub enum AdyenStatus {
Refused, Refused,
} }
#[derive(Debug, Clone, Serialize)]
pub enum Channel {
Web,
}
/// This implementation will be used only in Authorize, Automatic capture flow. /// This implementation will be used only in Authorize, Automatic capture flow.
/// It is also being used in Psync flow, However Psync will be called only after create payment call that too in redirect flow. /// It is also being used in Psync flow, However Psync will be called only after create payment call that too in redirect flow.
impl ForeignFrom<(bool, AdyenStatus)> for storage_enums::AttemptStatus { impl ForeignFrom<(bool, AdyenStatus)> for storage_enums::AttemptStatus {
@ -203,13 +209,14 @@ pub struct AdyenThreeDS {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum AdyenPaymentResponse { pub enum AdyenPaymentResponse {
AdyenResponse(AdyenResponse), Response(Response),
AdyenRedirectResponse(AdyenRedirectionResponse), RedirectResponse(RedirectionResponse),
RedirectionErrorResponse(RedirectionErrorResponse),
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AdyenResponse { pub struct Response {
psp_reference: String, psp_reference: String,
result_code: AdyenStatus, result_code: AdyenStatus,
amount: Option<Amount>, amount: Option<Amount>,
@ -219,9 +226,16 @@ pub struct AdyenResponse {
additional_data: Option<AdditionalData>, additional_data: Option<AdditionalData>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RedirectionErrorResponse {
result_code: AdyenStatus,
refusal_reason: String,
}
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AdyenRedirectionResponse { pub struct RedirectionResponse {
result_code: AdyenStatus, result_code: AdyenStatus,
action: AdyenRedirectionAction, action: AdyenRedirectionAction,
refusal_reason: Option<String>, refusal_reason: Option<String>,
@ -271,6 +285,8 @@ pub enum AdyenPaymentMethod<'a> {
Eps(Box<BankRedirectionWithIssuer<'a>>), Eps(Box<BankRedirectionWithIssuer<'a>>),
Giropay(Box<BankRedirectionPMData>), Giropay(Box<BankRedirectionPMData>),
Gpay(Box<AdyenGPay>), Gpay(Box<AdyenGPay>),
#[serde(rename = "gopay_wallet")]
GoPay(Box<GoPayData>),
Ideal(Box<BankRedirectionWithIssuer<'a>>), Ideal(Box<BankRedirectionWithIssuer<'a>>),
Mandate(Box<AdyenMandate>), Mandate(Box<AdyenMandate>),
Mbway(Box<MbwayData>), Mbway(Box<MbwayData>),
@ -623,6 +639,9 @@ pub struct AliPayHkData {
payment_type: PaymentType, payment_type: PaymentType,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GoPayData {}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdyenGPay { pub struct AdyenGPay {
#[serde(rename = "type")] #[serde(rename = "type")]
@ -685,6 +704,8 @@ pub enum PaymentType {
Eps, Eps,
Giropay, Giropay,
Googlepay, Googlepay,
#[serde(rename = "gopay_wallet")]
GoPay,
Ideal, Ideal,
Klarna, Klarna,
Mbway, Mbway,
@ -869,6 +890,7 @@ fn get_browser_info(
) -> Result<Option<AdyenBrowserInfo>, Error> { ) -> Result<Option<AdyenBrowserInfo>, Error> {
if item.auth_type == storage_enums::AuthenticationType::ThreeDs if item.auth_type == storage_enums::AuthenticationType::ThreeDs
|| item.payment_method == storage_enums::PaymentMethod::BankRedirect || item.payment_method == storage_enums::PaymentMethod::BankRedirect
|| item.request.payment_method_type == Some(storage_enums::PaymentMethodType::GoPay)
{ {
let info = item.request.get_browser_info()?; let info = item.request.get_browser_info()?;
Ok(Some(AdyenBrowserInfo { Ok(Some(AdyenBrowserInfo {
@ -900,6 +922,13 @@ fn get_additional_data(item: &types::PaymentsAuthorizeRouterData) -> Option<Addi
} }
} }
fn get_channel_type(pm_type: &Option<storage_enums::PaymentMethodType>) -> Option<Channel> {
pm_type.as_ref().and_then(|pmt| match pmt {
storage_enums::PaymentMethodType::GoPay => Some(Channel::Web),
_ => None,
})
}
fn get_amount_data(item: &types::PaymentsAuthorizeRouterData) -> Amount { fn get_amount_data(item: &types::PaymentsAuthorizeRouterData) -> Amount {
Amount { Amount {
currency: item.request.currency.to_string(), currency: item.request.currency.to_string(),
@ -1150,6 +1179,10 @@ impl<'a> TryFrom<&api::WalletData> for AdyenPaymentMethod<'a> {
}; };
Ok(AdyenPaymentMethod::AliPayHk(Box::new(alipay_hk_data))) Ok(AdyenPaymentMethod::AliPayHk(Box::new(alipay_hk_data)))
} }
api_models::payments::WalletData::GoPayRedirect(_) => {
let go_pay_data = GoPayData {};
Ok(AdyenPaymentMethod::GoPay(Box::new(go_pay_data)))
}
api_models::payments::WalletData::MbWayRedirect(data) => { api_models::payments::WalletData::MbWayRedirect(data) => {
let mbway_data = MbwayData { let mbway_data = MbwayData {
payment_type: PaymentType::Mbway, payment_type: PaymentType::Mbway,
@ -1435,6 +1468,7 @@ impl<'a>
line_items: None, line_items: None,
shopper_reference, shopper_reference,
store_payment_method, store_payment_method,
channel: None,
}) })
} }
} }
@ -1473,6 +1507,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::Card)> for AdyenPay
line_items: None, line_items: None,
shopper_reference, shopper_reference,
store_payment_method, store_payment_method,
channel: None,
}) })
} }
} }
@ -1521,6 +1556,7 @@ impl<'a>
line_items: None, line_items: None,
shopper_reference: None, shopper_reference: None,
store_payment_method: None, store_payment_method: None,
channel: None,
}; };
Ok(request) Ok(request)
} }
@ -1572,6 +1608,7 @@ impl<'a>
line_items, line_items,
shopper_reference, shopper_reference,
store_payment_method, store_payment_method,
channel: None,
}) })
} }
} }
@ -1631,6 +1668,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::WalletData)>
let additional_data = get_additional_data(item); let additional_data = get_additional_data(item);
let payment_method = AdyenPaymentMethod::try_from(wallet_data)?; let payment_method = AdyenPaymentMethod::try_from(wallet_data)?;
let shopper_interaction = AdyenShopperInteraction::from(item); let shopper_interaction = AdyenShopperInteraction::from(item);
let channel = get_channel_type(&item.request.payment_method_type);
let (recurring_processing_model, store_payment_method, shopper_reference) = let (recurring_processing_model, store_payment_method, shopper_reference) =
get_recurring_processing_model(item)?; get_recurring_processing_model(item)?;
let return_url = item.request.get_return_url()?; let return_url = item.request.get_return_url()?;
@ -1655,6 +1693,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::WalletData)>
line_items: None, line_items: None,
shopper_reference, shopper_reference,
store_payment_method, store_payment_method,
channel,
}) })
} }
} }
@ -1703,6 +1742,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::PayLaterData)>
line_items, line_items,
shopper_reference, shopper_reference,
store_payment_method, store_payment_method,
channel: None,
}) })
} }
} }
@ -1750,7 +1790,7 @@ impl TryFrom<types::PaymentsCancelResponseRouterData<AdyenCancelResponse>>
} }
pub fn get_adyen_response( pub fn get_adyen_response(
response: AdyenResponse, response: Response,
is_capture_manual: bool, is_capture_manual: bool,
status_code: u16, status_code: u16,
) -> errors::CustomResult< ) -> errors::CustomResult<
@ -1801,7 +1841,7 @@ pub fn get_adyen_response(
} }
pub fn get_redirection_response( pub fn get_redirection_response(
response: AdyenRedirectionResponse, response: RedirectionResponse,
is_manual_capture: bool, is_manual_capture: bool,
status_code: u16, status_code: u16,
) -> errors::CustomResult< ) -> errors::CustomResult<
@ -1855,6 +1895,38 @@ pub fn get_redirection_response(
Ok((status, error, payments_response_data)) Ok((status, error, payments_response_data))
} }
pub fn get_redirection_error_response(
response: RedirectionErrorResponse,
is_manual_capture: bool,
status_code: u16,
) -> errors::CustomResult<
(
storage_enums::AttemptStatus,
Option<types::ErrorResponse>,
types::PaymentsResponseData,
),
errors::ConnectorError,
> {
let status =
storage_enums::AttemptStatus::foreign_from((is_manual_capture, response.result_code));
let error = Some(types::ErrorResponse {
code: status.to_string(),
message: response.refusal_reason.clone(),
reason: Some(response.refusal_reason),
status_code,
});
// We don't get connector transaction id for redirections in Adyen.
let payments_response_data = types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::NoResponseId,
redirection_data: None,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
};
Ok((status, error, payments_response_data))
}
impl<F, Req> impl<F, Req>
TryFrom<( TryFrom<(
types::ResponseRouterData<F, AdyenPaymentResponse, Req, types::PaymentsResponseData>, types::ResponseRouterData<F, AdyenPaymentResponse, Req, types::PaymentsResponseData>,
@ -1871,12 +1943,15 @@ impl<F, Req>
let item = items.0; let item = items.0;
let is_manual_capture = items.1; let is_manual_capture = items.1;
let (status, error, payment_response_data) = match item.response { let (status, error, payment_response_data) = match item.response {
AdyenPaymentResponse::AdyenResponse(response) => { AdyenPaymentResponse::Response(response) => {
get_adyen_response(response, is_manual_capture, item.http_code)? get_adyen_response(response, is_manual_capture, item.http_code)?
} }
AdyenPaymentResponse::AdyenRedirectResponse(response) => { AdyenPaymentResponse::RedirectResponse(response) => {
get_redirection_response(response, is_manual_capture, item.http_code)? get_redirection_response(response, is_manual_capture, item.http_code)?
} }
AdyenPaymentResponse::RedirectionErrorResponse(response) => {
get_redirection_error_response(response, is_manual_capture, item.http_code)?
}
}; };
Ok(Self { Ok(Self {
@ -2182,7 +2257,7 @@ pub struct AdyenIncomingWebhook {
pub notification_items: Vec<AdyenItemObjectWH>, pub notification_items: Vec<AdyenItemObjectWH>,
} }
impl From<AdyenNotificationRequestItemWH> for AdyenResponse { impl From<AdyenNotificationRequestItemWH> for Response {
fn from(notif: AdyenNotificationRequestItemWH) -> Self { fn from(notif: AdyenNotificationRequestItemWH) -> Self {
Self { Self {
psp_reference: notif.psp_reference, psp_reference: notif.psp_reference,

View File

@ -171,6 +171,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::payments::AliPayQr, api_models::payments::AliPayQr,
api_models::payments::AliPayRedirection, api_models::payments::AliPayRedirection,
api_models::payments::AliPayHkRedirection, api_models::payments::AliPayHkRedirection,
api_models::payments::GoPayRedirection,
api_models::payments::MbWayRedirection, api_models::payments::MbWayRedirection,
api_models::payments::MobilePayRedirection, api_models::payments::MobilePayRedirection,
api_models::payments::WeChatPayRedirection, api_models::payments::WeChatPayRedirection,

View File

@ -174,7 +174,8 @@ impl ForeignFrom<api_enums::PaymentMethodType> for api_enums::PaymentMethod {
| api_enums::PaymentMethodType::MbWay | api_enums::PaymentMethodType::MbWay
| api_enums::PaymentMethodType::MobilePay | api_enums::PaymentMethodType::MobilePay
| api_enums::PaymentMethodType::SamsungPay | api_enums::PaymentMethodType::SamsungPay
| api_enums::PaymentMethodType::WeChatPay => Self::Wallet, | api_enums::PaymentMethodType::WeChatPay
| api_enums::PaymentMethodType::GoPay => Self::Wallet,
api_enums::PaymentMethodType::Affirm api_enums::PaymentMethodType::Affirm
| api_enums::PaymentMethodType::AfterpayClearpay | api_enums::PaymentMethodType::AfterpayClearpay
| api_enums::PaymentMethodType::Klarna | api_enums::PaymentMethodType::Klarna

View File

@ -145,10 +145,10 @@ async fn should_make_adyen_klarna_mandate_payment(
Ok(()) Ok(())
} }
async fn should_make_adyen_alipay_hk_payment(c: WebDriver) -> Result<(), WebDriverError> { async fn should_make_adyen_alipay_hk_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
let conn = AdyenSeleniumTest {}; let conn = AdyenSeleniumTest {};
conn.make_redirection_payment( conn.make_redirection_payment(
c, web_driver,
vec![ vec![
Event::Trigger(Trigger::Goto(&format!("{CHEKOUT_BASE_URL}/saved/162"))), Event::Trigger(Trigger::Goto(&format!("{CHEKOUT_BASE_URL}/saved/162"))),
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))), Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),

View File

@ -4347,6 +4347,9 @@
"on_session" "on_session"
] ]
}, },
"GoPayRedirection": {
"type": "object"
},
"GooglePayPaymentMethodInfo": { "GooglePayPaymentMethodInfo": {
"type": "object", "type": "object",
"required": [ "required": [
@ -6801,6 +6804,7 @@
"evoucher", "evoucher",
"giropay", "giropay",
"google_pay", "google_pay",
"go_pay",
"ideal", "ideal",
"interac", "interac",
"klarna", "klarna",
@ -8905,6 +8909,17 @@
} }
} }
}, },
{
"type": "object",
"required": [
"go_pay_redirect"
],
"properties": {
"go_pay_redirect": {
"$ref": "#/components/schemas/GoPayRedirection"
}
}
},
{ {
"type": "object", "type": "object",
"required": [ "required": [