feat(router): Add payments get-intent API for v2 (#6396)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Anurag Thakur
2024-10-30 21:26:23 +05:30
committed by GitHub
parent 33bc83fce4
commit c514608594
18 changed files with 571 additions and 196 deletions

View File

@ -1021,7 +1021,7 @@ pub async fn payments_intent_operation_core<F, Req, Op, D>(
) -> RouterResult<(D, Req, Option<domain::Customer>)>
where
F: Send + Clone + Sync,
Req: Authenticate + Clone,
Req: Clone,
Op: Operation<F, Req, Data = D> + Send + Sync,
D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
@ -1454,7 +1454,7 @@ pub async fn payments_intent_core<F, Res, Req, Op, D>(
where
F: Send + Clone + Sync,
Op: Operation<F, Req, Data = D> + Send + Sync + Clone,
Req: Debug + Authenticate + Clone,
Req: Debug + Clone,
D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
Res: transformers::ToResponse<F, D, Op>,
{

View File

@ -28,11 +28,12 @@ pub mod payments_incremental_authorization;
#[cfg(feature = "v1")]
pub mod tax_calculation;
#[cfg(feature = "v2")]
pub mod payment_create_intent;
#[cfg(feature = "v2")]
pub mod payment_confirm_intent;
#[cfg(feature = "v2")]
pub mod payment_create_intent;
#[cfg(feature = "v2")]
pub mod payment_get_intent;
use api_models::enums::FrmSuggestion;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
@ -45,6 +46,8 @@ use router_env::{instrument, tracing};
pub use self::payment_confirm_intent::PaymentIntentConfirm;
#[cfg(feature = "v2")]
pub use self::payment_create_intent::PaymentIntentCreate;
#[cfg(feature = "v2")]
pub use self::payment_get_intent::PaymentGetIntent;
pub use self::payment_response::PaymentResponse;
#[cfg(feature = "v1")]
pub use self::{

View File

@ -0,0 +1,231 @@
use std::marker::PhantomData;
use api_models::{enums::FrmSuggestion, payments::PaymentsGetIntentRequest};
use async_trait::async_trait;
use common_utils::errors::CustomResult;
use router_env::{instrument, tracing};
use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
core::{
errors::{self, RouterResult},
payments::{self, helpers, operations},
},
db::errors::StorageErrorExt,
routes::{app::ReqState, SessionState},
types::{
api, domain,
storage::{self, enums},
},
};
#[derive(Debug, Clone, Copy)]
pub struct PaymentGetIntent;
impl<F: Send + Clone> Operation<F, PaymentsGetIntentRequest> for &PaymentGetIntent {
type Data = payments::PaymentIntentData<F>;
fn to_validate_request(
&self,
) -> RouterResult<&(dyn ValidateRequest<F, PaymentsGetIntentRequest, Self::Data> + Send + Sync)>
{
Ok(*self)
}
fn to_get_tracker(
&self,
) -> RouterResult<&(dyn GetTracker<F, Self::Data, PaymentsGetIntentRequest> + Send + Sync)>
{
Ok(*self)
}
fn to_domain(&self) -> RouterResult<&(dyn Domain<F, PaymentsGetIntentRequest, Self::Data>)> {
Ok(*self)
}
fn to_update_tracker(
&self,
) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, PaymentsGetIntentRequest> + Send + Sync)>
{
Ok(*self)
}
}
impl<F: Send + Clone> Operation<F, PaymentsGetIntentRequest> for PaymentGetIntent {
type Data = payments::PaymentIntentData<F>;
fn to_validate_request(
&self,
) -> RouterResult<&(dyn ValidateRequest<F, PaymentsGetIntentRequest, Self::Data> + Send + Sync)>
{
Ok(self)
}
fn to_get_tracker(
&self,
) -> RouterResult<&(dyn GetTracker<F, Self::Data, PaymentsGetIntentRequest> + Send + Sync)>
{
Ok(self)
}
fn to_domain(&self) -> RouterResult<&dyn Domain<F, PaymentsGetIntentRequest, Self::Data>> {
Ok(self)
}
fn to_update_tracker(
&self,
) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, PaymentsGetIntentRequest> + Send + Sync)>
{
Ok(self)
}
}
type PaymentsGetIntentOperation<'b, F> =
BoxedOperation<'b, F, PaymentsGetIntentRequest, payments::PaymentIntentData<F>>;
#[async_trait]
impl<F: Send + Clone> GetTracker<F, payments::PaymentIntentData<F>, PaymentsGetIntentRequest>
for PaymentGetIntent
{
#[instrument(skip_all)]
async fn get_trackers<'a>(
&'a self,
state: &'a SessionState,
_payment_id: &common_utils::id_type::GlobalPaymentId,
request: &PaymentsGetIntentRequest,
merchant_account: &domain::MerchantAccount,
_profile: &domain::Profile,
key_store: &domain::MerchantKeyStore,
_header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
) -> RouterResult<
operations::GetTrackerResponse<
'a,
F,
PaymentsGetIntentRequest,
payments::PaymentIntentData<F>,
>,
> {
let db = &*state.store;
let key_manager_state = &state.into();
let storage_scheme = merchant_account.storage_scheme;
let payment_intent = db
.find_payment_intent_by_id(key_manager_state, &request.id, key_store, storage_scheme)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
let payment_data = payments::PaymentIntentData {
flow: PhantomData,
payment_intent,
};
let get_trackers_response = operations::GetTrackerResponse {
operation: Box::new(self),
payment_data,
};
Ok(get_trackers_response)
}
}
#[async_trait]
impl<F: Clone> UpdateTracker<F, payments::PaymentIntentData<F>, PaymentsGetIntentRequest>
for PaymentGetIntent
{
#[instrument(skip_all)]
async fn update_trackers<'b>(
&'b self,
_state: &'b SessionState,
_req_state: ReqState,
payment_data: payments::PaymentIntentData<F>,
_customer: Option<domain::Customer>,
_storage_scheme: enums::MerchantStorageScheme,
_updated_customer: Option<storage::CustomerUpdate>,
_key_store: &domain::MerchantKeyStore,
_frm_suggestion: Option<FrmSuggestion>,
_header_payload: hyperswitch_domain_models::payments::HeaderPayload,
) -> RouterResult<(
PaymentsGetIntentOperation<'b, F>,
payments::PaymentIntentData<F>,
)>
where
F: 'b + Send,
{
Ok((Box::new(self), payment_data))
}
}
impl<F: Send + Clone> ValidateRequest<F, PaymentsGetIntentRequest, payments::PaymentIntentData<F>>
for PaymentGetIntent
{
#[instrument(skip_all)]
fn validate_request<'a, 'b>(
&'b self,
_request: &PaymentsGetIntentRequest,
merchant_account: &'a domain::MerchantAccount,
) -> RouterResult<(
PaymentsGetIntentOperation<'b, F>,
operations::ValidateResult,
)> {
Ok((
Box::new(self),
operations::ValidateResult {
merchant_id: merchant_account.get_id().to_owned(),
storage_scheme: merchant_account.storage_scheme,
requeue: false,
},
))
}
}
#[async_trait]
impl<F: Clone + Send> Domain<F, PaymentsGetIntentRequest, payments::PaymentIntentData<F>>
for PaymentGetIntent
{
#[instrument(skip_all)]
async fn get_customer_details<'a>(
&'a self,
state: &SessionState,
payment_data: &mut payments::PaymentIntentData<F>,
merchant_key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
) -> CustomResult<
(
BoxedOperation<'a, F, PaymentsGetIntentRequest, payments::PaymentIntentData<F>>,
Option<domain::Customer>,
),
errors::StorageError,
> {
Ok((Box::new(self), None))
}
#[instrument(skip_all)]
async fn make_pm_data<'a>(
&'a self,
_state: &'a SessionState,
_payment_data: &mut payments::PaymentIntentData<F>,
_storage_scheme: enums::MerchantStorageScheme,
_merchant_key_store: &domain::MerchantKeyStore,
_customer: &Option<domain::Customer>,
_business_profile: &domain::Profile,
) -> RouterResult<(
PaymentsGetIntentOperation<'a, F>,
Option<domain::PaymentMethodData>,
Option<String>,
)> {
Ok((Box::new(self), None, None))
}
async fn get_connector<'a>(
&'a self,
_merchant_account: &domain::MerchantAccount,
state: &SessionState,
_request: &PaymentsGetIntentRequest,
_payment_intent: &storage::PaymentIntent,
_merchant_key_store: &domain::MerchantKeyStore,
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
helpers::get_connector_default(state, None).await
}
#[instrument(skip_all)]
async fn guard_payment_against_blocklist<'a>(
&'a self,
_state: &SessionState,
_merchant_account: &domain::MerchantAccount,
_key_store: &domain::MerchantKeyStore,
_payment_data: &mut payments::PaymentIntentData<F>,
) -> CustomResult<bool, errors::ApiErrorResponse> {
Ok(false)
}
}

View File

@ -755,7 +755,7 @@ where
}
#[cfg(feature = "v2")]
impl<F, Op, D> ToResponse<F, D, Op> for api::PaymentsCreateIntentResponse
impl<F, Op, D> ToResponse<F, D, Op> for api::PaymentsIntentResponse
where
F: Clone,
Op: Debug,

View File

@ -527,6 +527,10 @@ impl Payments {
web::resource("/confirm-intent")
.route(web::post().to(payments::payment_confirm_intent)),
)
.service(
web::resource("/get-intent")
.route(web::get().to(payments::payments_get_intent)),
)
.service(
web::resource("/create-external-sdk-tokens")
.route(web::post().to(payments::payments_connector_session)),

View File

@ -139,6 +139,7 @@ impl From<Flow> for ApiIdentifier {
| Flow::SessionUpdateTaxCalculation
| Flow::PaymentsConfirmIntent
| Flow::PaymentsCreateIntent
| Flow::PaymentsGetIntent
| Flow::PaymentsPostSessionTokens => Self::Payments,
Flow::PayoutsCreate

View File

@ -125,11 +125,11 @@ pub async fn payments_create_intent(
json_payload.into_inner(),
|state, auth: auth::AuthenticationDataV2, req, req_state| {
payments::payments_intent_core::<
api_types::CreateIntent,
payment_types::PaymentsCreateIntentResponse,
api_types::PaymentCreateIntent,
payment_types::PaymentsIntentResponse,
_,
_,
PaymentIntentData<api_types::CreateIntent>,
PaymentIntentData<api_types::PaymentCreateIntent>,
>(
state,
req_state,
@ -156,6 +156,57 @@ pub async fn payments_create_intent(
.await
}
#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsGetIntent, payment_id))]
pub async fn payments_get_intent(
state: web::Data<app::AppState>,
req: actix_web::HttpRequest,
path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
use api_models::payments::PaymentsGetIntentRequest;
use hyperswitch_domain_models::payments::PaymentIntentData;
let flow = Flow::PaymentsGetIntent;
let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
Ok(headers) => headers,
Err(err) => {
return api::log_and_return_error_response(err);
}
};
let payload = PaymentsGetIntentRequest {
id: path.into_inner(),
};
Box::pin(api::server_wrap(
flow,
state,
&req,
payload,
|state, auth: auth::AuthenticationDataV2, req, req_state| {
payments::payments_intent_core::<
api_types::PaymentGetIntent,
payment_types::PaymentsIntentResponse,
_,
_,
PaymentIntentData<api_types::PaymentGetIntent>,
>(
state,
req_state,
auth.merchant_account,
auth.profile,
auth.key_store,
payments::operations::PaymentGetIntent,
req,
header_payload.clone(),
)
},
&auth::HeaderAuth(auth::ApiKeyAuth),
api_locking::LockAction::NotApplicable,
))
.await
}
#[cfg(feature = "v1")]
#[instrument(skip(state, req), fields(flow = ?Flow::PaymentsStart, payment_id))]
pub async fn payments_start(

View File

@ -1259,10 +1259,8 @@ impl Authenticate for api_models::payments::PaymentsIncrementalAuthorizationRequ
impl Authenticate for api_models::payments::PaymentsStartRequest {}
// impl Authenticate for api_models::payments::PaymentsApproveRequest {}
impl Authenticate for api_models::payments::PaymentsRejectRequest {}
#[cfg(feature = "v2")]
impl Authenticate for api_models::payments::PaymentsCreateIntentRequest {}
// #[cfg(feature = "v2")]
// impl Authenticate for api_models::payments::PaymentsCreateIntentResponse {}
// impl Authenticate for api_models::payments::PaymentsIntentResponse {}
pub fn build_redirection_form(
form: &RedirectForm,

View File

@ -19,13 +19,13 @@ pub use api_models::payments::{
VerifyResponse, WalletData,
};
#[cfg(feature = "v2")]
pub use api_models::payments::{PaymentsCreateIntentRequest, PaymentsCreateIntentResponse};
pub use api_models::payments::{PaymentsCreateIntentRequest, PaymentsIntentResponse};
use error_stack::ResultExt;
pub use hyperswitch_domain_models::router_flow_types::payments::{
Approve, Authorize, AuthorizeSessionToken, Balance, CalculateTax, Capture, CompleteAuthorize,
CreateConnectorCustomer, CreateIntent, IncrementalAuthorization, InitPayment, PSync,
PaymentMethodToken, PostProcessing, PostSessionTokens, PreProcessing, Reject, SdkSessionUpdate,
Session, SetupMandate, Void,
CreateConnectorCustomer, IncrementalAuthorization, InitPayment, PSync, PaymentCreateIntent,
PaymentGetIntent, PaymentMethodToken, PostProcessing, PostSessionTokens, PreProcessing, Reject,
SdkSessionUpdate, Session, SetupMandate, Void,
};
pub use hyperswitch_interfaces::api::payments::{
ConnectorCustomer, MandateSetup, Payment, PaymentApprove, PaymentAuthorize,