mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(connector): [Adyen] implement Gopay for Adyen (#1557)
Co-authored-by: Sangamesh Kulkarni <59434228+Sangamesh26@users.noreply.github.com>
This commit is contained in:
@ -952,6 +952,8 @@ pub enum WalletData {
|
||||
AliPayRedirect(AliPayRedirection),
|
||||
/// The wallet data for Ali Pay HK redirect
|
||||
AliPayHkRedirect(AliPayHkRedirection),
|
||||
/// The wallet data for GoPay redirect
|
||||
GoPayRedirect(GoPayRedirection),
|
||||
/// The wallet data for Apple pay
|
||||
ApplePay(ApplePayWalletData),
|
||||
/// Wallet data for apple pay redirect flow
|
||||
@ -1036,6 +1038,9 @@ pub struct AliPayRedirection {}
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
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)]
|
||||
pub struct MobilePayRedirection {}
|
||||
|
||||
|
||||
@ -570,6 +570,7 @@ pub enum PaymentMethodType {
|
||||
Evoucher,
|
||||
Giropay,
|
||||
GooglePay,
|
||||
GoPay,
|
||||
Ideal,
|
||||
Interac,
|
||||
Klarna,
|
||||
|
||||
@ -1554,6 +1554,7 @@ impl From<PaymentMethodType> for PaymentMethod {
|
||||
PaymentMethodType::Evoucher => Self::Reward,
|
||||
PaymentMethodType::Giropay => Self::BankRedirect,
|
||||
PaymentMethodType::GooglePay => Self::Wallet,
|
||||
PaymentMethodType::GoPay => Self::Wallet,
|
||||
PaymentMethodType::Ideal => Self::BankRedirect,
|
||||
PaymentMethodType::Klarna => Self::PayLater,
|
||||
PaymentMethodType::MbWay => Self::Wallet,
|
||||
|
||||
@ -907,7 +907,7 @@ impl api::IncomingWebhook for Adyen {
|
||||
let notif = get_webhook_object_from_body(request.body)
|
||||
.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)
|
||||
.into_report()
|
||||
|
||||
@ -116,6 +116,7 @@ pub struct AdyenPaymentRequest<'a> {
|
||||
delivery_address: Option<Address>,
|
||||
country_code: Option<api_enums::CountryAlpha2>,
|
||||
line_items: Option<Vec<LineItem>>,
|
||||
channel: Option<Channel>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@ -145,6 +146,11 @@ pub enum AdyenStatus {
|
||||
Refused,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum Channel {
|
||||
Web,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
impl ForeignFrom<(bool, AdyenStatus)> for storage_enums::AttemptStatus {
|
||||
@ -203,13 +209,14 @@ pub struct AdyenThreeDS {
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum AdyenPaymentResponse {
|
||||
AdyenResponse(AdyenResponse),
|
||||
AdyenRedirectResponse(AdyenRedirectionResponse),
|
||||
Response(Response),
|
||||
RedirectResponse(RedirectionResponse),
|
||||
RedirectionErrorResponse(RedirectionErrorResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdyenResponse {
|
||||
pub struct Response {
|
||||
psp_reference: String,
|
||||
result_code: AdyenStatus,
|
||||
amount: Option<Amount>,
|
||||
@ -219,9 +226,16 @@ pub struct AdyenResponse {
|
||||
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)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdyenRedirectionResponse {
|
||||
pub struct RedirectionResponse {
|
||||
result_code: AdyenStatus,
|
||||
action: AdyenRedirectionAction,
|
||||
refusal_reason: Option<String>,
|
||||
@ -271,6 +285,8 @@ pub enum AdyenPaymentMethod<'a> {
|
||||
Eps(Box<BankRedirectionWithIssuer<'a>>),
|
||||
Giropay(Box<BankRedirectionPMData>),
|
||||
Gpay(Box<AdyenGPay>),
|
||||
#[serde(rename = "gopay_wallet")]
|
||||
GoPay(Box<GoPayData>),
|
||||
Ideal(Box<BankRedirectionWithIssuer<'a>>),
|
||||
Mandate(Box<AdyenMandate>),
|
||||
Mbway(Box<MbwayData>),
|
||||
@ -623,6 +639,9 @@ pub struct AliPayHkData {
|
||||
payment_type: PaymentType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GoPayData {}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AdyenGPay {
|
||||
#[serde(rename = "type")]
|
||||
@ -685,6 +704,8 @@ pub enum PaymentType {
|
||||
Eps,
|
||||
Giropay,
|
||||
Googlepay,
|
||||
#[serde(rename = "gopay_wallet")]
|
||||
GoPay,
|
||||
Ideal,
|
||||
Klarna,
|
||||
Mbway,
|
||||
@ -869,6 +890,7 @@ fn get_browser_info(
|
||||
) -> Result<Option<AdyenBrowserInfo>, Error> {
|
||||
if item.auth_type == storage_enums::AuthenticationType::ThreeDs
|
||||
|| item.payment_method == storage_enums::PaymentMethod::BankRedirect
|
||||
|| item.request.payment_method_type == Some(storage_enums::PaymentMethodType::GoPay)
|
||||
{
|
||||
let info = item.request.get_browser_info()?;
|
||||
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 {
|
||||
Amount {
|
||||
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)))
|
||||
}
|
||||
api_models::payments::WalletData::GoPayRedirect(_) => {
|
||||
let go_pay_data = GoPayData {};
|
||||
Ok(AdyenPaymentMethod::GoPay(Box::new(go_pay_data)))
|
||||
}
|
||||
api_models::payments::WalletData::MbWayRedirect(data) => {
|
||||
let mbway_data = MbwayData {
|
||||
payment_type: PaymentType::Mbway,
|
||||
@ -1435,6 +1468,7 @@ impl<'a>
|
||||
line_items: None,
|
||||
shopper_reference,
|
||||
store_payment_method,
|
||||
channel: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1473,6 +1507,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::Card)> for AdyenPay
|
||||
line_items: None,
|
||||
shopper_reference,
|
||||
store_payment_method,
|
||||
channel: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1521,6 +1556,7 @@ impl<'a>
|
||||
line_items: None,
|
||||
shopper_reference: None,
|
||||
store_payment_method: None,
|
||||
channel: None,
|
||||
};
|
||||
Ok(request)
|
||||
}
|
||||
@ -1572,6 +1608,7 @@ impl<'a>
|
||||
line_items,
|
||||
shopper_reference,
|
||||
store_payment_method,
|
||||
channel: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1631,6 +1668,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::WalletData)>
|
||||
let additional_data = get_additional_data(item);
|
||||
let payment_method = AdyenPaymentMethod::try_from(wallet_data)?;
|
||||
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) =
|
||||
get_recurring_processing_model(item)?;
|
||||
let return_url = item.request.get_return_url()?;
|
||||
@ -1655,6 +1693,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::WalletData)>
|
||||
line_items: None,
|
||||
shopper_reference,
|
||||
store_payment_method,
|
||||
channel,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1703,6 +1742,7 @@ impl<'a> TryFrom<(&types::PaymentsAuthorizeRouterData, &api::PayLaterData)>
|
||||
line_items,
|
||||
shopper_reference,
|
||||
store_payment_method,
|
||||
channel: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1750,7 +1790,7 @@ impl TryFrom<types::PaymentsCancelResponseRouterData<AdyenCancelResponse>>
|
||||
}
|
||||
|
||||
pub fn get_adyen_response(
|
||||
response: AdyenResponse,
|
||||
response: Response,
|
||||
is_capture_manual: bool,
|
||||
status_code: u16,
|
||||
) -> errors::CustomResult<
|
||||
@ -1801,7 +1841,7 @@ pub fn get_adyen_response(
|
||||
}
|
||||
|
||||
pub fn get_redirection_response(
|
||||
response: AdyenRedirectionResponse,
|
||||
response: RedirectionResponse,
|
||||
is_manual_capture: bool,
|
||||
status_code: u16,
|
||||
) -> errors::CustomResult<
|
||||
@ -1855,6 +1895,38 @@ pub fn get_redirection_response(
|
||||
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>
|
||||
TryFrom<(
|
||||
types::ResponseRouterData<F, AdyenPaymentResponse, Req, types::PaymentsResponseData>,
|
||||
@ -1871,12 +1943,15 @@ impl<F, Req>
|
||||
let item = items.0;
|
||||
let is_manual_capture = items.1;
|
||||
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)?
|
||||
}
|
||||
AdyenPaymentResponse::AdyenRedirectResponse(response) => {
|
||||
AdyenPaymentResponse::RedirectResponse(response) => {
|
||||
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 {
|
||||
@ -2182,7 +2257,7 @@ pub struct AdyenIncomingWebhook {
|
||||
pub notification_items: Vec<AdyenItemObjectWH>,
|
||||
}
|
||||
|
||||
impl From<AdyenNotificationRequestItemWH> for AdyenResponse {
|
||||
impl From<AdyenNotificationRequestItemWH> for Response {
|
||||
fn from(notif: AdyenNotificationRequestItemWH) -> Self {
|
||||
Self {
|
||||
psp_reference: notif.psp_reference,
|
||||
|
||||
@ -171,6 +171,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::AliPayQr,
|
||||
api_models::payments::AliPayRedirection,
|
||||
api_models::payments::AliPayHkRedirection,
|
||||
api_models::payments::GoPayRedirection,
|
||||
api_models::payments::MbWayRedirection,
|
||||
api_models::payments::MobilePayRedirection,
|
||||
api_models::payments::WeChatPayRedirection,
|
||||
|
||||
@ -174,7 +174,8 @@ impl ForeignFrom<api_enums::PaymentMethodType> for api_enums::PaymentMethod {
|
||||
| api_enums::PaymentMethodType::MbWay
|
||||
| api_enums::PaymentMethodType::MobilePay
|
||||
| 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::AfterpayClearpay
|
||||
| api_enums::PaymentMethodType::Klarna
|
||||
|
||||
@ -145,10 +145,10 @@ async fn should_make_adyen_klarna_mandate_payment(
|
||||
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 {};
|
||||
conn.make_redirection_payment(
|
||||
c,
|
||||
web_driver,
|
||||
vec![
|
||||
Event::Trigger(Trigger::Goto(&format!("{CHEKOUT_BASE_URL}/saved/162"))),
|
||||
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
|
||||
|
||||
@ -4347,6 +4347,9 @@
|
||||
"on_session"
|
||||
]
|
||||
},
|
||||
"GoPayRedirection": {
|
||||
"type": "object"
|
||||
},
|
||||
"GooglePayPaymentMethodInfo": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -6801,6 +6804,7 @@
|
||||
"evoucher",
|
||||
"giropay",
|
||||
"google_pay",
|
||||
"go_pay",
|
||||
"ideal",
|
||||
"interac",
|
||||
"klarna",
|
||||
@ -8905,6 +8909,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"go_pay_redirect"
|
||||
],
|
||||
"properties": {
|
||||
"go_pay_redirect": {
|
||||
"$ref": "#/components/schemas/GoPayRedirection"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
Reference in New Issue
Block a user