mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(connector): [Adyen] Add support for Blik (#1727)
This commit is contained in:
@ -1353,6 +1353,7 @@ pub enum NextActionType {
|
|||||||
InvokeSdkClient,
|
InvokeSdkClient,
|
||||||
TriggerApi,
|
TriggerApi,
|
||||||
DisplayBankTransferInformation,
|
DisplayBankTransferInformation,
|
||||||
|
DisplayWaitScreen,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, ToSchema)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, ToSchema)]
|
||||||
@ -1376,6 +1377,11 @@ pub enum NextActionData {
|
|||||||
#[schema(value_type = String)]
|
#[schema(value_type = String)]
|
||||||
voucher_details: VoucherNextStepData,
|
voucher_details: VoucherNextStepData,
|
||||||
},
|
},
|
||||||
|
/// Contains duration for displaying a wait screen, wait screen with timer is displayed by sdk
|
||||||
|
WaitScreenInformation {
|
||||||
|
display_from_timestamp: i128,
|
||||||
|
display_to_timestamp: Option<i128>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
@ -1400,6 +1406,12 @@ pub struct QrCodeNextStepsInstruction {
|
|||||||
pub image_data_url: Url,
|
pub image_data_url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
|
pub struct WaitScreenInstructions {
|
||||||
|
pub display_from_timestamp: i128,
|
||||||
|
pub display_to_timestamp: Option<i128>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum BankTransferInstructions {
|
pub enum BankTransferInstructions {
|
||||||
|
|||||||
@ -840,6 +840,8 @@ pub enum PaymentExperience {
|
|||||||
LinkWallet,
|
LinkWallet,
|
||||||
/// Contains the data for invoking the sdk client for completing the payment.
|
/// Contains the data for invoking the sdk client for completing the payment.
|
||||||
InvokePaymentApp,
|
InvokePaymentApp,
|
||||||
|
/// Contains the data for displaying wait screen
|
||||||
|
DisplayWaitScreen,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|||||||
@ -773,6 +773,10 @@ pub enum StripeNextAction {
|
|||||||
DisplayVoucherInformation {
|
DisplayVoucherInformation {
|
||||||
voucher_details: payments::VoucherNextStepData,
|
voucher_details: payments::VoucherNextStepData,
|
||||||
},
|
},
|
||||||
|
WaitScreenInformation {
|
||||||
|
display_from_timestamp: i128,
|
||||||
|
display_to_timestamp: Option<i128>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_stripe_next_action(
|
pub(crate) fn into_stripe_next_action(
|
||||||
@ -802,6 +806,13 @@ pub(crate) fn into_stripe_next_action(
|
|||||||
payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
|
payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
|
||||||
StripeNextAction::DisplayVoucherInformation { voucher_details }
|
StripeNextAction::DisplayVoucherInformation { voucher_details }
|
||||||
}
|
}
|
||||||
|
payments::NextActionData::WaitScreenInformation {
|
||||||
|
display_from_timestamp,
|
||||||
|
display_to_timestamp,
|
||||||
|
} => StripeNextAction::WaitScreenInformation {
|
||||||
|
display_from_timestamp,
|
||||||
|
display_to_timestamp,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -374,6 +374,10 @@ pub enum StripeNextAction {
|
|||||||
DisplayVoucherInformation {
|
DisplayVoucherInformation {
|
||||||
voucher_details: payments::VoucherNextStepData,
|
voucher_details: payments::VoucherNextStepData,
|
||||||
},
|
},
|
||||||
|
WaitScreenInformation {
|
||||||
|
display_from_timestamp: i128,
|
||||||
|
display_to_timestamp: Option<i128>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_stripe_next_action(
|
pub(crate) fn into_stripe_next_action(
|
||||||
@ -403,6 +407,13 @@ pub(crate) fn into_stripe_next_action(
|
|||||||
payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
|
payments::NextActionData::DisplayVoucherInformation { voucher_details } => {
|
||||||
StripeNextAction::DisplayVoucherInformation { voucher_details }
|
StripeNextAction::DisplayVoucherInformation { voucher_details }
|
||||||
}
|
}
|
||||||
|
payments::NextActionData::WaitScreenInformation {
|
||||||
|
display_from_timestamp,
|
||||||
|
display_to_timestamp,
|
||||||
|
} => StripeNextAction::WaitScreenInformation {
|
||||||
|
display_from_timestamp,
|
||||||
|
display_to_timestamp,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use error_stack::ResultExt;
|
|||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time::PrimitiveDateTime;
|
use time::{Duration, OffsetDateTime, PrimitiveDateTime};
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
#[cfg(feature = "payouts")]
|
||||||
use crate::connector::utils::AddressDetailsData;
|
use crate::connector::utils::AddressDetailsData;
|
||||||
@ -2367,13 +2367,10 @@ pub fn get_connector_metadata(
|
|||||||
response: &NextActionResponse,
|
response: &NextActionResponse,
|
||||||
) -> errors::CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
|
) -> errors::CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
|
||||||
let connector_metadata = match response.action.type_of_response {
|
let connector_metadata = match response.action.type_of_response {
|
||||||
ActionType::QrCode => {
|
ActionType::QrCode => get_qr_metadata(response),
|
||||||
let metadata = get_qr_metadata(response);
|
ActionType::Await => get_wait_screen_metadata(response),
|
||||||
Some(metadata)
|
_ => Ok(None),
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
.transpose()
|
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
|
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
|
||||||
|
|
||||||
Ok(connector_metadata)
|
Ok(connector_metadata)
|
||||||
@ -2381,7 +2378,7 @@ pub fn get_connector_metadata(
|
|||||||
|
|
||||||
pub fn get_qr_metadata(
|
pub fn get_qr_metadata(
|
||||||
response: &NextActionResponse,
|
response: &NextActionResponse,
|
||||||
) -> errors::CustomResult<serde_json::Value, errors::ConnectorError> {
|
) -> errors::CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
|
||||||
let image_data = response
|
let image_data = response
|
||||||
.action
|
.action
|
||||||
.qr_code_data
|
.qr_code_data
|
||||||
@ -2396,12 +2393,41 @@ pub fn get_qr_metadata(
|
|||||||
|
|
||||||
let qr_code_instructions = payments::QrCodeNextStepsInstruction { image_data_url };
|
let qr_code_instructions = payments::QrCodeNextStepsInstruction { image_data_url };
|
||||||
|
|
||||||
common_utils::ext_traits::Encode::<payments::QrCodeNextStepsInstruction>::encode_to_value(
|
Some(common_utils::ext_traits::Encode::<
|
||||||
&qr_code_instructions,
|
payments::QrCodeNextStepsInstruction,
|
||||||
)
|
>::encode_to_value(&qr_code_instructions))
|
||||||
|
.transpose()
|
||||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct WaitScreenData {
|
||||||
|
display_from_timestamp: i128,
|
||||||
|
display_to_timestamp: Option<i128>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_wait_screen_metadata(
|
||||||
|
next_action: &NextActionResponse,
|
||||||
|
) -> errors::CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
|
||||||
|
match next_action.action.payment_method_type {
|
||||||
|
PaymentType::Blik => {
|
||||||
|
let current_time = OffsetDateTime::now_utc().unix_timestamp_nanos();
|
||||||
|
Ok(Some(serde_json::json!(WaitScreenData {
|
||||||
|
display_from_timestamp: current_time,
|
||||||
|
display_to_timestamp: Some(current_time + Duration::minutes(1).whole_nanoseconds())
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
PaymentType::Mbway => {
|
||||||
|
let current_time = OffsetDateTime::now_utc().unix_timestamp_nanos();
|
||||||
|
Ok(Some(serde_json::json!(WaitScreenData {
|
||||||
|
display_from_timestamp: current_time,
|
||||||
|
display_to_timestamp: None
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<F, Req>
|
impl<F, Req>
|
||||||
TryFrom<(
|
TryFrom<(
|
||||||
types::ResponseRouterData<F, AdyenPaymentResponse, Req, types::PaymentsResponseData>,
|
types::ResponseRouterData<F, AdyenPaymentResponse, Req, types::PaymentsResponseData>,
|
||||||
|
|||||||
@ -431,6 +431,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize {
|
|||||||
api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None,
|
api_models::payments::NextActionData::ThirdPartySdkSessionToken { .. } => None,
|
||||||
api_models::payments::NextActionData::QrCodeInformation{..} => None,
|
api_models::payments::NextActionData::QrCodeInformation{..} => None,
|
||||||
api_models::payments::NextActionData::DisplayVoucherInformation{ .. } => None,
|
api_models::payments::NextActionData::DisplayVoucherInformation{ .. } => None,
|
||||||
|
api_models::payments::NextActionData::WaitScreenInformation{..} => None,
|
||||||
})
|
})
|
||||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
.into_report()
|
.into_report()
|
||||||
|
|||||||
@ -375,13 +375,17 @@ where
|
|||||||
|
|
||||||
let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;
|
let next_action_voucher = voucher_next_steps_check(payment_attempt.clone())?;
|
||||||
|
|
||||||
let next_action_containing_qr_code =
|
let next_action_containing_qr_code_url =
|
||||||
qr_code_next_steps_check(payment_attempt.clone())?;
|
qr_code_next_steps_check(payment_attempt.clone())?;
|
||||||
|
|
||||||
|
let next_action_containing_wait_screen =
|
||||||
|
wait_screen_next_steps_check(payment_attempt.clone())?;
|
||||||
|
|
||||||
if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
|
if payment_intent.status == enums::IntentStatus::RequiresCustomerAction
|
||||||
|| bank_transfer_next_steps.is_some()
|
|| bank_transfer_next_steps.is_some()
|
||||||
|| next_action_voucher.is_some()
|
|| next_action_voucher.is_some()
|
||||||
|| next_action_containing_qr_code.is_some()
|
|| next_action_containing_qr_code_url.is_some()
|
||||||
|
|| next_action_containing_wait_screen.is_some()
|
||||||
{
|
{
|
||||||
next_action_response = bank_transfer_next_steps
|
next_action_response = bank_transfer_next_steps
|
||||||
.map(|bank_transfer| {
|
.map(|bank_transfer| {
|
||||||
@ -394,11 +398,17 @@ where
|
|||||||
voucher_details: voucher_data,
|
voucher_details: voucher_data,
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.or(next_action_containing_qr_code.map(|qr_code_data| {
|
.or(next_action_containing_qr_code_url.map(|qr_code_data| {
|
||||||
api_models::payments::NextActionData::QrCodeInformation {
|
api_models::payments::NextActionData::QrCodeInformation {
|
||||||
image_data_url: qr_code_data.image_data_url,
|
image_data_url: qr_code_data.image_data_url,
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
.or(next_action_containing_wait_screen.map(|wait_screen_data| {
|
||||||
|
api_models::payments::NextActionData::WaitScreenInformation {
|
||||||
|
display_from_timestamp: wait_screen_data.display_from_timestamp,
|
||||||
|
display_to_timestamp: wait_screen_data.display_to_timestamp,
|
||||||
|
}
|
||||||
|
}))
|
||||||
.or(redirection_data.map(|_| {
|
.or(redirection_data.map(|_| {
|
||||||
api_models::payments::NextActionData::RedirectToUrl {
|
api_models::payments::NextActionData::RedirectToUrl {
|
||||||
redirect_to_url: helpers::create_startpay_url(
|
redirect_to_url: helpers::create_startpay_url(
|
||||||
@ -624,6 +634,20 @@ pub fn qr_code_next_steps_check(
|
|||||||
Ok(qr_code_instructions)
|
Ok(qr_code_instructions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wait_screen_next_steps_check(
|
||||||
|
payment_attempt: storage::PaymentAttempt,
|
||||||
|
) -> RouterResult<Option<api_models::payments::WaitScreenInstructions>> {
|
||||||
|
let display_info_with_timer_steps: Option<
|
||||||
|
Result<api_models::payments::WaitScreenInstructions, _>,
|
||||||
|
> = payment_attempt
|
||||||
|
.connector_metadata
|
||||||
|
.map(|metadata| metadata.parse_value("WaitScreenInstructions"));
|
||||||
|
|
||||||
|
let display_info_with_timer_instructions =
|
||||||
|
display_info_with_timer_steps.transpose().ok().flatten();
|
||||||
|
Ok(display_info_with_timer_instructions)
|
||||||
|
}
|
||||||
|
|
||||||
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
||||||
fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
|
fn foreign_from(item: (storage::PaymentIntent, storage::PaymentAttempt)) -> Self {
|
||||||
let pi = item.0;
|
let pi = item.0;
|
||||||
|
|||||||
@ -332,21 +332,6 @@ async fn should_make_adyen_eps_payment(web_driver: WebDriver) -> Result<(), WebD
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn should_make_adyen_blik_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
|
|
||||||
let conn = AdyenSeleniumTest {};
|
|
||||||
conn.make_redirection_payment(
|
|
||||||
web_driver,
|
|
||||||
vec![
|
|
||||||
Event::Trigger(Trigger::Goto(&format!("{CHEKOUT_BASE_URL}/saved/64"))),
|
|
||||||
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
|
|
||||||
Event::Assert(Assert::IsPresent("Status")),
|
|
||||||
Event::Assert(Assert::IsPresent("processing")), //final status of this payment method will remain in processing state
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn should_make_adyen_bancontact_card_payment(
|
async fn should_make_adyen_bancontact_card_payment(
|
||||||
web_driver: WebDriver,
|
web_driver: WebDriver,
|
||||||
) -> Result<(), WebDriverError> {
|
) -> Result<(), WebDriverError> {
|
||||||
@ -634,6 +619,21 @@ async fn should_make_adyen_swish_payment(web_driver: WebDriver) -> Result<(), We
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn should_make_adyen_blik_payment(driver: WebDriver) -> Result<(), WebDriverError> {
|
||||||
|
let conn = AdyenSeleniumTest {};
|
||||||
|
conn.make_redirection_payment(
|
||||||
|
driver,
|
||||||
|
vec![
|
||||||
|
Event::Trigger(Trigger::Goto(&format!("{CHEKOUT_BASE_URL}/saved/64"))),
|
||||||
|
Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
|
||||||
|
Event::Assert(Assert::IsPresent("Next Action Type")),
|
||||||
|
Event::Assert(Assert::IsPresent("wait_screen_information")),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
@ -752,12 +752,6 @@ fn should_make_adyen_eps_payment_test() {
|
|||||||
tester!(should_make_adyen_eps_payment);
|
tester!(should_make_adyen_eps_payment);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn should_make_adyen_blik_payment_test() {
|
|
||||||
tester!(should_make_adyen_blik_payment);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn should_make_adyen_bancontact_card_payment_test() {
|
fn should_make_adyen_bancontact_card_payment_test() {
|
||||||
@ -808,6 +802,12 @@ fn should_make_adyen_dana_payment_test() {
|
|||||||
tester!(should_make_adyen_dana_payment);
|
tester!(should_make_adyen_dana_payment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn should_make_adyen_blik_payment_test() {
|
||||||
|
tester!(should_make_adyen_blik_payment);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn should_make_adyen_online_banking_fpx_payment_test() {
|
fn should_make_adyen_online_banking_fpx_payment_test() {
|
||||||
|
|||||||
@ -6395,6 +6395,29 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"description": "Contains duration for displaying a wait screen, wait screen with timer is displayed by sdk",
|
||||||
|
"required": [
|
||||||
|
"display_from_timestamp",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"display_from_timestamp": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"display_to_timestamp": {
|
||||||
|
"type": "integer",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"wait_screen_information"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"discriminator": {
|
"discriminator": {
|
||||||
@ -6408,7 +6431,8 @@
|
|||||||
"display_qr_code",
|
"display_qr_code",
|
||||||
"invoke_sdk_client",
|
"invoke_sdk_client",
|
||||||
"trigger_api",
|
"trigger_api",
|
||||||
"display_bank_transfer_information"
|
"display_bank_transfer_information",
|
||||||
|
"display_wait_screen"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"NoThirdPartySdkSessionResponse": {
|
"NoThirdPartySdkSessionResponse": {
|
||||||
@ -6827,7 +6851,8 @@
|
|||||||
"display_qr_code",
|
"display_qr_code",
|
||||||
"one_click",
|
"one_click",
|
||||||
"link_wallet",
|
"link_wallet",
|
||||||
"invoke_payment_app"
|
"invoke_payment_app",
|
||||||
|
"display_wait_screen"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"PaymentIdType": {
|
"PaymentIdType": {
|
||||||
|
|||||||
Reference in New Issue
Block a user