feat(payments_v2): implement create and confirm intent flow (#7106)

This commit is contained in:
Sanchith Hegde
2025-02-05 19:07:11 +05:30
committed by GitHub
parent ea1888677d
commit 67ea754e38
34 changed files with 1659 additions and 1115 deletions

View File

@ -3,13 +3,15 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
#[cfg(feature = "v2")]
use super::{
PaymentStartRedirectionRequest, PaymentsConfirmIntentResponse, PaymentsCreateIntentRequest,
PaymentsGetIntentRequest, PaymentsIntentResponse,
PaymentsGetIntentRequest, PaymentsIntentResponse, PaymentsRequest,
};
#[cfg(all(
any(feature = "v2", feature = "v1"),
not(feature = "payment_methods_v2")
))]
use crate::payment_methods::CustomerPaymentMethodsListResponse;
#[cfg(feature = "v1")]
use crate::payments::{PaymentListResponse, PaymentListResponseV2};
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use crate::{events, payment_methods::CustomerPaymentMethodsListResponse};
use crate::{
@ -23,14 +25,14 @@ use crate::{
payments::{
self, ExtendedCardInfoResponse, PaymentIdType, PaymentListConstraints,
PaymentListFilterConstraints, PaymentListFilters, PaymentListFiltersV2,
PaymentListResponse, PaymentListResponseV2, PaymentsAggregateResponse,
PaymentsApproveRequest, PaymentsCancelRequest, PaymentsCaptureRequest,
PaymentsCompleteAuthorizeRequest, PaymentsDynamicTaxCalculationRequest,
PaymentsDynamicTaxCalculationResponse, PaymentsExternalAuthenticationRequest,
PaymentsExternalAuthenticationResponse, PaymentsIncrementalAuthorizationRequest,
PaymentsManualUpdateRequest, PaymentsManualUpdateResponse,
PaymentsPostSessionTokensRequest, PaymentsPostSessionTokensResponse, PaymentsRejectRequest,
PaymentsResponse, PaymentsRetrieveRequest, PaymentsSessionResponse, PaymentsStartRequest,
PaymentsAggregateResponse, PaymentsApproveRequest, PaymentsCancelRequest,
PaymentsCaptureRequest, PaymentsCompleteAuthorizeRequest,
PaymentsDynamicTaxCalculationRequest, PaymentsDynamicTaxCalculationResponse,
PaymentsExternalAuthenticationRequest, PaymentsExternalAuthenticationResponse,
PaymentsIncrementalAuthorizationRequest, PaymentsManualUpdateRequest,
PaymentsManualUpdateResponse, PaymentsPostSessionTokensRequest,
PaymentsPostSessionTokensResponse, PaymentsRejectRequest, PaymentsResponse,
PaymentsRetrieveRequest, PaymentsSessionResponse, PaymentsStartRequest,
RedirectionResponse,
},
};
@ -150,6 +152,22 @@ impl ApiEventMetric for PaymentsCreateIntentRequest {
}
}
#[cfg(feature = "v2")]
impl ApiEventMetric for PaymentsRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
None
}
}
#[cfg(feature = "v2")]
impl ApiEventMetric for PaymentsResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Payment {
payment_id: self.id.clone(),
})
}
}
#[cfg(feature = "v2")]
impl ApiEventMetric for PaymentsGetIntentRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
@ -355,12 +373,14 @@ impl ApiEventMetric for PaymentListConstraints {
}
}
#[cfg(feature = "v1")]
impl ApiEventMetric for PaymentListResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::ResourceListAPI)
}
}
#[cfg(feature = "v1")]
impl ApiEventMetric for PaymentListResponseV2 {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::ResourceListAPI)

View File

@ -4414,6 +4414,7 @@ pub struct ReceiverDetails {
amount_remaining: Option<i64>,
}
#[cfg(feature = "v1")]
#[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema, router_derive::PolymorphicSchema)]
#[generate_schemas(PaymentsCreateResponseOpenApi)]
pub struct PaymentsResponse {
@ -4787,6 +4788,271 @@ pub struct PaymentsConfirmIntentRequest {
pub browser_info: Option<common_utils::types::BrowserInformation>,
}
// This struct contains the union of fields in `PaymentsCreateIntentRequest` and
// `PaymentsConfirmIntentRequest`
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
#[cfg(feature = "v2")]
pub struct PaymentsRequest {
/// The amount details for the payment
pub amount_details: AmountDetails,
/// Unique identifier for the payment. This ensures idempotency for multiple payments
/// that have been done by a single merchant.
#[schema(
value_type = Option<String>,
min_length = 30,
max_length = 30,
example = "pay_mbabizu24mvu3mela5njyhpit4"
)]
pub merchant_reference_id: Option<id_type::PaymentReferenceId>,
/// The routing algorithm id to be used for the payment
#[schema(value_type = Option<String>)]
pub routing_algorithm_id: Option<id_type::RoutingId>,
#[schema(value_type = Option<CaptureMethod>, example = "automatic")]
pub capture_method: Option<api_enums::CaptureMethod>,
#[schema(value_type = Option<AuthenticationType>, example = "no_three_ds", default = "no_three_ds")]
pub authentication_type: Option<api_enums::AuthenticationType>,
/// The billing details of the payment. This address will be used for invoicing.
pub billing: Option<Address>,
/// The shipping address for the payment
pub shipping: Option<Address>,
/// The identifier for the customer
#[schema(
min_length = 32,
max_length = 64,
example = "12345_cus_01926c58bc6e77c09e809964e72af8c8",
value_type = String
)]
pub customer_id: Option<id_type::GlobalCustomerId>,
/// Set to `present` to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be `absent` when merchant's doing merchant initiated payments and customer is not present while doing the payment.
#[schema(example = "present", value_type = Option<PresenceOfCustomerDuringPayment>)]
pub customer_present: Option<common_enums::PresenceOfCustomerDuringPayment>,
/// A description for the payment
#[schema(example = "It's my first payment request", value_type = Option<String>)]
pub description: Option<common_utils::types::Description>,
/// The URL to which you want the user to be redirected after the completion of the payment operation
#[schema(value_type = Option<String>, example = "https://hyperswitch.io")]
pub return_url: Option<common_utils::types::Url>,
#[schema(value_type = Option<FutureUsage>, example = "off_session")]
pub setup_future_usage: Option<api_enums::FutureUsage>,
/// Apply MIT exemption for a payment
#[schema(value_type = Option<MitExemptionRequest>)]
pub apply_mit_exemption: Option<common_enums::MitExemptionRequest>,
/// For non-card charges, you can use this value as the complete description that appears on your customers statements. Must contain at least one letter, maximum 22 characters.
#[schema(max_length = 22, example = "Hyperswitch Router", value_type = Option<String>)]
pub statement_descriptor: Option<common_utils::types::StatementDescriptor>,
/// Use this object to capture the details about the different products for which the payment is being made. The sum of amount across different products here should be equal to the overall payment amount
#[schema(value_type = Option<Vec<OrderDetailsWithAmount>>, example = r#"[{
"product_name": "Apple iPhone 16",
"quantity": 1,
"amount" : 69000
"product_img_link" : "https://dummy-img-link.com"
}]"#)]
pub order_details: Option<Vec<OrderDetailsWithAmount>>,
/// Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent
#[schema(value_type = Option<Vec<PaymentMethodType>>)]
pub allowed_payment_method_types: Option<Vec<api_enums::PaymentMethodType>>,
/// Metadata is useful for storing additional, unstructured information on an object.
#[schema(value_type = Option<Object>, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)]
pub metadata: Option<pii::SecretSerdeValue>,
/// Some connectors like Apple pay, Airwallex and Noon might require some additional information, find specific details in the child attributes below.
pub connector_metadata: Option<ConnectorMetadata>,
/// Additional data that might be required by hyperswitch based on the requested features by the merchants.
pub feature_metadata: Option<FeatureMetadata>,
/// Whether to generate the payment link for this payment or not (if applicable)
#[schema(value_type = Option<EnablePaymentLinkRequest>)]
pub payment_link_enabled: Option<common_enums::EnablePaymentLinkRequest>,
/// Configure a custom payment link for the particular payment
#[schema(value_type = Option<PaymentLinkConfigRequest>)]
pub payment_link_config: Option<admin::PaymentLinkConfigRequest>,
///Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it.
#[schema(value_type = Option<RequestIncrementalAuthorization>)]
pub request_incremental_authorization: Option<common_enums::RequestIncrementalAuthorization>,
///Will be used to expire client secret after certain amount of time to be supplied in seconds, if not sent it will be taken from profile config
///(900) for 15 mins
#[schema(example = 900)]
pub session_expiry: Option<u32>,
/// Additional data related to some frm(Fraud Risk Management) connectors
#[schema(value_type = Option<Object>, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)]
pub frm_metadata: Option<pii::SecretSerdeValue>,
/// Whether to perform external authentication (if applicable)
#[schema(value_type = Option<External3dsAuthenticationRequest>)]
pub request_external_three_ds_authentication:
Option<common_enums::External3dsAuthenticationRequest>,
/// The payment instrument data to be used for the payment
pub payment_method_data: PaymentMethodDataRequest,
/// The payment method type to be used for the payment. This should match with the `payment_method_data` provided
#[schema(value_type = PaymentMethod, example = "card")]
pub payment_method_type: api_enums::PaymentMethod,
/// The payment method subtype to be used for the payment. This should match with the `payment_method_data` provided
#[schema(value_type = PaymentMethodType, example = "apple_pay")]
pub payment_method_subtype: api_enums::PaymentMethodType,
/// This "CustomerAcceptance" object is passed during Payments-Confirm request, it enlists the type, time, and mode of acceptance properties related to an acceptance done by the customer. The customer_acceptance sub object is usually passed by the SDK or client.
#[schema(value_type = Option<CustomerAcceptance>)]
pub customer_acceptance: Option<CustomerAcceptance>,
/// Additional details required by 3DS 2.0
#[schema(value_type = Option<BrowserInformation>)]
pub browser_info: Option<common_utils::types::BrowserInformation>,
}
#[cfg(feature = "v2")]
impl From<&PaymentsRequest> for PaymentsCreateIntentRequest {
fn from(request: &PaymentsRequest) -> Self {
Self {
amount_details: request.amount_details.clone(),
merchant_reference_id: request.merchant_reference_id.clone(),
routing_algorithm_id: request.routing_algorithm_id.clone(),
capture_method: request.capture_method,
authentication_type: request.authentication_type,
billing: request.billing.clone(),
shipping: request.shipping.clone(),
customer_id: request.customer_id.clone(),
customer_present: request.customer_present.clone(),
description: request.description.clone(),
return_url: request.return_url.clone(),
setup_future_usage: request.setup_future_usage,
apply_mit_exemption: request.apply_mit_exemption.clone(),
statement_descriptor: request.statement_descriptor.clone(),
order_details: request.order_details.clone(),
allowed_payment_method_types: request.allowed_payment_method_types.clone(),
metadata: request.metadata.clone(),
connector_metadata: request.connector_metadata.clone(),
feature_metadata: request.feature_metadata.clone(),
payment_link_enabled: request.payment_link_enabled.clone(),
payment_link_config: request.payment_link_config.clone(),
request_incremental_authorization: request.request_incremental_authorization,
session_expiry: request.session_expiry,
frm_metadata: request.frm_metadata.clone(),
request_external_three_ds_authentication: request
.request_external_three_ds_authentication
.clone(),
}
}
}
#[cfg(feature = "v2")]
impl From<&PaymentsRequest> for PaymentsConfirmIntentRequest {
fn from(request: &PaymentsRequest) -> Self {
Self {
return_url: request.return_url.clone(),
payment_method_data: request.payment_method_data.clone(),
payment_method_type: request.payment_method_type,
payment_method_subtype: request.payment_method_subtype,
shipping: request.shipping.clone(),
customer_acceptance: request.customer_acceptance.clone(),
browser_info: request.browser_info.clone(),
}
}
}
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentsResponse {
/// Unique identifier for the payment. This ensures idempotency for multiple payments
/// that have been done by a single merchant.
#[schema(
min_length = 32,
max_length = 64,
example = "12345_pay_01926c58bc6e77c09e809964e72af8c8",
value_type = String,
)]
pub id: id_type::GlobalPaymentId,
#[schema(value_type = IntentStatus, example = "success")]
pub status: api_enums::IntentStatus,
/// Amount related information for this payment and attempt
pub amount: PaymentAmountDetailsResponse,
/// The identifier for the customer
#[schema(
min_length = 32,
max_length = 64,
example = "12345_cus_01926c58bc6e77c09e809964e72af8c8",
value_type = String
)]
pub customer_id: Option<id_type::GlobalCustomerId>,
/// The connector used for the payment
#[schema(example = "stripe")]
pub connector: String,
/// It's a token used for client side verification.
#[schema(value_type = String)]
pub client_secret: common_utils::types::ClientSecret,
/// Time when the payment was created
#[schema(example = "2022-09-10T10:11:12Z")]
#[serde(with = "common_utils::custom_serde::iso8601")]
pub created: PrimitiveDateTime,
/// The payment method information provided for making a payment
#[schema(value_type = Option<PaymentMethodDataResponseWithBilling>)]
#[serde(serialize_with = "serialize_payment_method_data_response")]
pub payment_method_data: Option<PaymentMethodDataResponseWithBilling>,
/// The payment method type for this payment attempt
#[schema(value_type = PaymentMethod, example = "wallet")]
pub payment_method_type: api_enums::PaymentMethod,
#[schema(value_type = PaymentMethodType, example = "apple_pay")]
pub payment_method_subtype: api_enums::PaymentMethodType,
/// Additional information required for redirection
pub next_action: Option<NextActionData>,
/// A unique identifier for a payment provided by the connector
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_transaction_id: Option<String>,
/// reference(Identifier) to the payment at connector side
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_reference_id: Option<String>,
/// Connector token information that can be used to make payments directly by the merchant.
pub connector_token_details: Option<ConnectorTokenDetails>,
/// Identifier of the connector ( merchant connector account ) which was chosen to make the payment
#[schema(value_type = String)]
pub merchant_connector_id: id_type::MerchantConnectorAccountId,
/// The browser information used for this payment
#[schema(value_type = Option<BrowserInformation>)]
pub browser_info: Option<common_utils::types::BrowserInformation>,
/// Error details for the payment if any
pub error: Option<ErrorDetails>,
}
// Serialize is implemented because, this will be serialized in the api events.
// Usually request types should not have serialize implemented.
//
@ -4885,6 +5151,9 @@ pub struct PaymentsConfirmIntentResponse {
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_reference_id: Option<String>,
/// Connector token information that can be used to make payments directly by the merchant.
pub connector_token_details: Option<ConnectorTokenDetails>,
/// Identifier of the connector ( merchant connector account ) which was chosen to make the payment
#[schema(value_type = String)]
pub merchant_connector_id: id_type::MerchantConnectorAccountId,
@ -4897,6 +5166,15 @@ pub struct PaymentsConfirmIntentResponse {
pub error: Option<ErrorDetails>,
}
/// Token information that can be used to initiate transactions by the merchant.
#[cfg(feature = "v2")]
#[derive(Debug, Serialize, ToSchema)]
pub struct ConnectorTokenDetails {
/// A token that can be used to make payments directly with the connector.
#[schema(example = "pm_9UhMqBMEOooRIvJFFdeW")]
pub token: String,
}
// TODO: have a separate response for detailed, summarized
/// Response for Payment Intent Confirm
#[cfg(feature = "v2")]
@ -5104,6 +5382,7 @@ pub struct PaymentListConstraints {
pub created_gte: Option<PrimitiveDateTime>,
}
#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct PaymentListResponse {
/// The number of payments included in the list
@ -5130,6 +5409,7 @@ pub struct IncrementalAuthorizationResponse {
pub previously_authorized_amount: MinorUnit,
}
#[cfg(feature = "v1")]
#[derive(Clone, Debug, serde::Serialize)]
pub struct PaymentListResponseV2 {
/// The number of payments included in the list for given constraints