From 2af8526bf55b641fa38fdb7cc77156cbb743fe59 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 6 Nov 2024 14:52:26 +0530 Subject: [PATCH 01/26] feat(core): Add payments update-intent API for v2 --- .../payments/payments--update-intent.mdx | 3 + api-reference-v2/mint.json | 1 + api-reference-v2/openapi_spec.json | 255 +++++++++++++ crates/api_models/src/payments.rs | 115 ++++++ crates/diesel_models/src/kv.rs | 21 +- crates/diesel_models/src/payment_intent.rs | 155 ++------ .../hyperswitch_domain_models/src/payments.rs | 12 + .../src/payments/payment_intent.rs | 314 +++++++++------- .../src/router_flow_types/payments.rs | 3 + crates/openapi/src/openapi_v2.rs | 2 + crates/openapi/src/routes/payments.rs | 31 ++ crates/router/src/core/payments.rs | 243 +++++++++++- crates/router/src/core/payments/operations.rs | 4 + .../operations/payment_update_intent.rs | 353 ++++++++++++++++++ crates/router/src/routes/app.rs | 4 + crates/router/src/routes/lock_utils.rs | 1 + crates/router/src/routes/payments.rs | 59 +++ crates/router/src/types/api/payments.rs | 8 +- crates/router_env/src/logger/types.rs | 2 + .../src/payments/payment_intent.rs | 8 +- 20 files changed, 1321 insertions(+), 273 deletions(-) create mode 100644 api-reference-v2/api-reference/payments/payments--update-intent.mdx create mode 100644 crates/router/src/core/payments/operations/payment_update_intent.rs diff --git a/api-reference-v2/api-reference/payments/payments--update-intent.mdx b/api-reference-v2/api-reference/payments/payments--update-intent.mdx new file mode 100644 index 000000000000..4c295b3f8347 --- /dev/null +++ b/api-reference-v2/api-reference/payments/payments--update-intent.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /v2/payments/{id}/update-intent +--- \ No newline at end of file diff --git a/api-reference-v2/mint.json b/api-reference-v2/mint.json index c0723a63f3a6..9366bac71fa3 100644 --- a/api-reference-v2/mint.json +++ b/api-reference-v2/mint.json @@ -38,6 +38,7 @@ "pages": [ "api-reference/payments/payments--create-intent", "api-reference/payments/payments--get-intent", + "api-reference/payments/payments--update-intent", "api-reference/payments/payments--session-token", "api-reference/payments/payments--confirm-intent", "api-reference/payments/payments--get" diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 0385aa55a20d..fdc6784c2745 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1911,6 +1911,67 @@ ] } }, + "/v2/payments/{id}/update-intent": { + "post": { + "tags": [ + "Payments" + ], + "summary": "Payments - Update Intent", + "description": "**Update a payment intent object**\n\nYou will require the 'API - Key' from the Hyperswitch dashboard to make the call.", + "operationId": "Update a Payment Intent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The unique identifier for the Payment Intent", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentsUpdateIntentRequest" + }, + "examples": { + "Update a payment intent with minimal fields": { + "value": { + "amount_details": { + "currency": "USD", + "order_amount": 6540 + } + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Payment Intent Updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentsIntentResponse" + } + } + } + }, + "404": { + "description": "Payment Intent Not Found" + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, "/v2/payments/{id}/confirm-intent": { "post": { "tags": [ @@ -15256,6 +15317,200 @@ } } }, + "PaymentsUpdateIntentRequest": { + "type": "object", + "properties": { + "amount_details": { + "allOf": [ + { + "$ref": "#/components/schemas/AmountDetails" + } + ], + "nullable": true + }, + "merchant_reference_id": { + "type": "string", + "description": "Unique identifier for the payment. This ensures idempotency for multiple payments\nthat have been done by a single merchant.", + "example": "pay_mbabizu24mvu3mela5njyhpit4", + "nullable": true, + "maxLength": 30, + "minLength": 30 + }, + "routing_algorithm_id": { + "type": "string", + "description": "The routing algorithm id to be used for the payment", + "nullable": true + }, + "capture_method": { + "allOf": [ + { + "$ref": "#/components/schemas/CaptureMethod" + } + ], + "nullable": true + }, + "authentication_type": { + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationType" + } + ], + "default": "no_three_ds", + "nullable": true + }, + "billing": { + "allOf": [ + { + "$ref": "#/components/schemas/Address" + } + ], + "nullable": true + }, + "shipping": { + "allOf": [ + { + "$ref": "#/components/schemas/Address" + } + ], + "nullable": true + }, + "customer_id": { + "type": "string", + "description": "The identifier for the customer", + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "nullable": true, + "maxLength": 64, + "minLength": 1 + }, + "customer_present": { + "allOf": [ + { + "$ref": "#/components/schemas/PresenceOfCustomerDuringPayment" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "description": "A description for the payment", + "example": "It's my first payment request", + "nullable": true + }, + "return_url": { + "type": "string", + "description": "The URL to which you want the user to be redirected after the completion of the payment operation", + "example": "https://hyperswitch.io", + "nullable": true + }, + "setup_future_usage": { + "allOf": [ + { + "$ref": "#/components/schemas/FutureUsage" + } + ], + "nullable": true + }, + "apply_mit_exemption": { + "allOf": [ + { + "$ref": "#/components/schemas/MitExemptionRequest" + } + ], + "nullable": true + }, + "statement_descriptor": { + "type": "string", + "description": "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.", + "example": "Hyperswitch Router", + "nullable": true, + "maxLength": 22 + }, + "order_details": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrderDetailsWithAmount" + }, + "description": "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", + "example": "[{\n \"product_name\": \"Apple iPhone 16\",\n \"quantity\": 1,\n \"amount\" : 69000\n \"product_img_link\" : \"https://dummy-img-link.com\"\n }]", + "nullable": true + }, + "allowed_payment_method_types": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentMethodType" + }, + "description": "Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent", + "nullable": true + }, + "metadata": { + "type": "object", + "description": "Metadata is useful for storing additional, unstructured information on an object.", + "nullable": true + }, + "connector_metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectorMetadata" + } + ], + "nullable": true + }, + "feature_metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/FeatureMetadata" + } + ], + "nullable": true + }, + "payment_link_enabled": { + "allOf": [ + { + "$ref": "#/components/schemas/EnablePaymentLinkRequest" + } + ], + "nullable": true + }, + "payment_link_config": { + "allOf": [ + { + "$ref": "#/components/schemas/PaymentLinkConfigRequest" + } + ], + "nullable": true + }, + "request_incremental_authorization": { + "allOf": [ + { + "$ref": "#/components/schemas/RequestIncrementalAuthorization" + } + ], + "nullable": true + }, + "session_expiry": { + "type": "integer", + "format": "int32", + "description": "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\n(900) for 15 mins", + "example": 900, + "nullable": true, + "minimum": 0 + }, + "frm_metadata": { + "type": "object", + "description": "Additional data related to some frm(Fraud Risk Management) connectors", + "nullable": true + }, + "request_external_three_ds_authentication": { + "allOf": [ + { + "$ref": "#/components/schemas/External3dsAuthenticationRequest" + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, "PayoutActionRequest": { "type": "object" }, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 567d601c45c1..b158f7fb5301 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -296,6 +296,121 @@ pub struct PaymentsGetIntentRequest { pub id: id_type::GlobalPaymentId, } +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] +#[serde(deny_unknown_fields)] +#[cfg(feature = "v2")] +pub struct PaymentsUpdateIntentRequest { + pub amount_details: Option, + + /// Unique identifier for the payment. This ensures idempotency for multiple payments + /// that have been done by a single merchant. + #[schema( + value_type = Option, + min_length = 30, + max_length = 30, + example = "pay_mbabizu24mvu3mela5njyhpit4" + )] + pub merchant_reference_id: Option, + + /// The routing algorithm id to be used for the payment + #[schema(value_type = Option)] + pub routing_algorithm_id: Option, + + #[schema(value_type = Option, example = "automatic")] + pub capture_method: Option, + + #[schema(value_type = Option, example = "no_three_ds", default = "no_three_ds")] + pub authentication_type: Option, + + /// The billing details of the payment. This address will be used for invoicing. + #[schema(value_type = Option
)] + pub billing: Option
, + + /// The shipping address for the payment + #[schema(value_type = Option
)] + pub shipping: Option
, + + /// The identifier for the customer + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, + + /// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. + #[schema(example = true, value_type = Option)] + pub customer_present: Option, + + /// A description for the payment + #[schema(example = "It's my first payment request", value_type = Option)] + pub description: Option, + + /// The URL to which you want the user to be redirected after the completion of the payment operation + #[schema(value_type = Option, example = "https://hyperswitch.io")] + pub return_url: Option, + + #[schema(value_type = Option, example = "off_session")] + pub setup_future_usage: Option, + + /// Apply MIT exemption for a payment + #[schema(value_type = Option)] + pub apply_mit_exemption: Option, + + // TODO: Verify this is required + // pub payment_method_token: Option, + /// 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)] + pub statement_descriptor: Option, + + /// 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>, example = r#"[{ + "product_name": "Apple iPhone 16", + "quantity": 1, + "amount" : 69000 + "product_img_link" : "https://dummy-img-link.com" + }]"#)] + pub order_details: Option>, + + /// Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent + #[schema(value_type = Option>)] + pub allowed_payment_method_types: Option, + + /// Metadata is useful for storing additional, unstructured information on an object. + #[schema(value_type = Option, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)] + pub metadata: Option, + + /// Some connectors like Apple pay, Airwallex and Noon might require some additional information, find specific details in the child attributes below. + #[schema(value_type = Option)] + pub connector_metadata: Option, + + /// Additional data that might be required by hyperswitch based on the requested features by the merchants. + #[schema(value_type = Option)] + pub feature_metadata: Option, + + /// Whether to generate the payment link for this payment or not (if applicable) + #[schema(value_type = Option)] + pub payment_link_enabled: Option, + + /// Configure a custom payment link for the particular payment + #[schema(value_type = Option)] + pub payment_link_config: Option, + + /// Request an incremental authorization, i.e., increase the authorized amount on a confirmed payment before you capture it. + #[schema(value_type = Option)] + pub request_incremental_authorization: Option, + + /// 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(value_type = Option, example = 900)] + pub session_expiry: Option, + + /// Additional data related to some frm(Fraud Risk Management) connectors + #[schema(value_type = Option, example = r#"{ "coverage_request" : "fraud", "fulfillment_method" : "delivery" }"#)] + pub frm_metadata: Option, + + /// Whether to perform external authentication (if applicable) + #[schema(value_type = Option)] + pub request_external_three_ds_authentication: + Option, +} + #[derive(Debug, serde::Serialize, Clone, ToSchema)] #[serde(deny_unknown_fields)] #[cfg(feature = "v2")] diff --git a/crates/diesel_models/src/kv.rs b/crates/diesel_models/src/kv.rs index 7c5c3a8de178..801592712928 100644 --- a/crates/diesel_models/src/kv.rs +++ b/crates/diesel_models/src/kv.rs @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "v2")] use crate::payment_attempt::PaymentAttemptUpdateInternal; +#[cfg(feature = "v1")] +use crate::payment_intent::PaymentIntentUpdate; #[cfg(feature = "v2")] use crate::payment_intent::PaymentIntentUpdateInternal; use crate::{ @@ -10,7 +12,7 @@ use crate::{ customers::{Customer, CustomerNew, CustomerUpdateInternal}, errors, payment_attempt::{PaymentAttempt, PaymentAttemptNew, PaymentAttemptUpdate}, - payment_intent::{PaymentIntentNew, PaymentIntentUpdate}, + payment_intent::PaymentIntentNew, payout_attempt::{PayoutAttempt, PayoutAttemptNew, PayoutAttemptUpdate}, payouts::{Payouts, PayoutsNew, PayoutsUpdate}, refund::{Refund, RefundNew, RefundUpdate}, @@ -115,11 +117,9 @@ impl DBOperation { DBResult::PaymentIntent(Box::new(a.orig.update(conn, a.update_data).await?)) } #[cfg(feature = "v2")] - Updateable::PaymentIntentUpdate(a) => DBResult::PaymentIntent(Box::new( - a.orig - .update(conn, PaymentIntentUpdateInternal::from(a.update_data)) - .await?, - )), + Updateable::PaymentIntentUpdate(a) => { + DBResult::PaymentIntent(Box::new(a.orig.update(conn, a.update_data).await?)) + } #[cfg(feature = "v1")] Updateable::PaymentAttemptUpdate(a) => DBResult::PaymentAttempt(Box::new( a.orig.update_with_attempt_id(conn, a.update_data).await?, @@ -247,13 +247,20 @@ pub struct AddressUpdateMems { pub orig: Address, pub update_data: AddressUpdateInternal, } - +#[cfg(feature = "v1")] #[derive(Debug, Serialize, Deserialize)] pub struct PaymentIntentUpdateMems { pub orig: PaymentIntent, pub update_data: PaymentIntentUpdate, } +#[cfg(feature = "v2")] +#[derive(Debug, Serialize, Deserialize)] +pub struct PaymentIntentUpdateMems { + pub orig: PaymentIntent, + pub update_data: PaymentIntentUpdateInternal, +} + #[derive(Debug, Serialize, Deserialize)] pub struct PaymentAttemptUpdateMems { pub orig: PaymentAttempt, diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 18248048110a..0a89e3044110 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -352,22 +352,6 @@ pub struct PaymentIntentNew { pub skip_external_tax_calculation: Option, } -#[cfg(feature = "v2")] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum PaymentIntentUpdate { - /// Update the payment intent details on payment intent confirmation, before calling the connector - ConfirmIntent { - status: storage_enums::IntentStatus, - active_attempt_id: common_utils::id_type::GlobalAttemptId, - updated_by: String, - }, - /// Update the payment intent details on payment intent confirmation, after calling the connector - ConfirmIntentPostUpdate { - status: storage_enums::IntentStatus, - updated_by: String, - }, -} - #[cfg(feature = "v1")] #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PaymentIntentUpdate { @@ -509,34 +493,43 @@ pub struct PaymentIntentUpdateFields { // TODO: uncomment fields as necessary #[cfg(feature = "v2")] -#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] +#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)] #[diesel(table_name = payment_intent)] pub struct PaymentIntentUpdateInternal { - // pub amount: Option, - // pub currency: Option, pub status: Option, - // pub amount_captured: Option, - // pub customer_id: Option, - // pub return_url: Option<>, - // pub setup_future_usage: Option, - // pub metadata: Option, - pub modified_at: PrimitiveDateTime, pub active_attempt_id: Option, - // pub description: Option, - // pub statement_descriptor: Option, - // #[diesel(deserialize_as = super::OptionalDieselArray)] - // pub order_details: Option>, - // pub attempt_count: Option, + pub modified_at: PrimitiveDateTime, + pub amount: Option, + pub currency: Option, + pub merchant_reference_id: Option, + pub routing_algorithm_id: Option, + pub capture_method: Option, + pub authentication_type: Option, + pub billing_address: Option, + pub shipping_address: Option, + pub customer_id: Option, + pub customer_present: Option, + pub description: Option, + pub return_url: Option, + pub setup_future_usage: Option, + pub apply_mit_exemption: Option, + pub statement_descriptor: Option, + pub order_details: Option>, + pub allowed_payment_method_types: Option, + pub metadata: Option, + pub connector_metadata: Option, + pub feature_metadata: Option, + // TODO: Should we rename the field instead? + #[diesel(column_name = enable_payment_link)] + pub payment_link_enabled: Option, + // TODO: Check this type + // pub payment_link_config: Option, + pub request_incremental_authorization: Option, + pub session_expiry: Option, + pub frm_metadata: Option, + // Setting it to common_enums::External3dsAuthenticationRequest gives error on `AsChangeset` derivation + pub request_external_three_ds_authentication: Option, pub updated_by: String, - // pub surcharge_applicable: Option, - // pub authorization_count: Option, - // pub session_expiry: Option, - // pub request_external_three_ds_authentication: Option, - // pub frm_metadata: Option, - // pub customer_details: Option, - // pub billing_address: Option, - // pub shipping_address: Option, - // pub frm_merchant_decision: Option, } #[cfg(feature = "v1")] @@ -582,66 +575,6 @@ pub struct PaymentIntentUpdateInternal { pub tax_details: Option, } -#[cfg(feature = "v2")] -impl PaymentIntentUpdate { - pub fn apply_changeset(self, source: PaymentIntent) -> PaymentIntent { - let PaymentIntentUpdateInternal { - // amount, - // currency, - status, - // amount_captured, - // customer_id, - // return_url, - // setup_future_usage, - // metadata, - modified_at: _, - active_attempt_id, - // description, - // statement_descriptor, - // order_details, - // attempt_count, - // frm_merchant_decision, - updated_by, - // surcharge_applicable, - // authorization_count, - // session_expiry, - // request_external_three_ds_authentication, - // frm_metadata, - // customer_details, - // billing_address, - // shipping_address, - } = self.into(); - PaymentIntent { - // amount: amount.unwrap_or(source.amount), - // currency: currency.unwrap_or(source.currency), - status: status.unwrap_or(source.status), - // amount_captured: amount_captured.or(source.amount_captured), - // customer_id: customer_id.or(source.customer_id), - // return_url: return_url.or(source.return_url), - // setup_future_usage: setup_future_usage.or(source.setup_future_usage), - // metadata: metadata.or(source.metadata), - modified_at: common_utils::date_time::now(), - active_attempt_id: active_attempt_id.or(source.active_attempt_id), - // description: description.or(source.description), - // statement_descriptor: statement_descriptor.or(source.statement_descriptor), - // order_details: order_details.or(source.order_details), - // attempt_count: attempt_count.unwrap_or(source.attempt_count), - // frm_merchant_decision: frm_merchant_decision.or(source.frm_merchant_decision), - updated_by, - // surcharge_applicable: surcharge_applicable.or(source.surcharge_applicable), - // authorization_count: authorization_count.or(source.authorization_count), - // session_expiry: session_expiry.or(source.session_expiry), - // request_external_three_ds_authentication: request_external_three_ds_authentication - // .or(source.request_external_three_ds_authentication), - // frm_metadata: frm_metadata.or(source.frm_metadata), - // customer_details: customer_details.or(source.customer_details), - // billing_address: billing_address.or(source.billing_address), - // shipping_address: shipping_address.or(source.shipping_address), - ..source - } - } -} - #[cfg(feature = "v1")] impl PaymentIntentUpdate { pub fn apply_changeset(self, source: PaymentIntent) -> PaymentIntent { @@ -732,30 +665,6 @@ impl PaymentIntentUpdate { } } -#[cfg(feature = "v2")] -impl From for PaymentIntentUpdateInternal { - fn from(payment_intent_update: PaymentIntentUpdate) -> Self { - match payment_intent_update { - PaymentIntentUpdate::ConfirmIntent { - status, - active_attempt_id, - updated_by, - } => Self { - status: Some(status), - active_attempt_id: Some(active_attempt_id), - modified_at: common_utils::date_time::now(), - updated_by, - }, - PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self { - status: Some(status), - active_attempt_id: None, - modified_at: common_utils::date_time::now(), - updated_by, - }, - } - } -} - #[cfg(feature = "v1")] impl From for PaymentIntentUpdateInternal { fn from(payment_intent_update: PaymentIntentUpdate) -> Self { diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 8b4c351daa35..2deab5ad1f5b 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -15,6 +15,7 @@ use diesel_models::payment_intent::TaxDetails; #[cfg(feature = "v2")] use error_stack::ResultExt; use masking::Secret; +use payment_intent::PaymentIntentUpdate; use router_derive::ToEncryption; use rustc_hash::FxHashMap; use serde_json::Value; @@ -555,3 +556,14 @@ where /// This will depend on the payment status and the force sync flag in the request pub should_sync_with_connector: bool, } + +#[cfg(feature = "v2")] +#[derive(Clone)] +pub struct PaymentUpdateData +where + F: Clone, +{ + pub flow: PhantomData, + pub payment_intent: PaymentIntent, + pub payment_intent_update: PaymentIntentUpdate, +} diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 4f2053ec6f99..6b876880fe5a 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,4 +1,5 @@ -use common_enums as storage_enums; +use api_models::payments::{Address, OrderDetailsWithAmount}; +use common_enums::{self as storage_enums, External3dsAuthenticationRequest}; #[cfg(feature = "v2")] use common_utils::ext_traits::{Encode, ValueExt}; use common_utils::{ @@ -11,7 +12,7 @@ use common_utils::{ type_name, types::{ keymanager::{self, KeyManagerState, ToEncryptable}, - MinorUnit, + MinorUnit, StatementDescriptor, }, }; #[cfg(feature = "v2")] @@ -287,39 +288,41 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, -} + UpdateIntent { + // TODO: Add more amount_details fields + amount: Option, + currency: Option, -#[cfg(feature = "v2")] -#[derive(Clone, Debug, Default)] -pub struct PaymentIntentUpdateInternal { - pub amount: Option, - pub currency: Option, - pub status: Option, - pub amount_captured: Option, - pub customer_id: Option, - pub return_url: Option, - pub setup_future_usage: Option, - pub off_session: Option, - pub metadata: Option, - pub modified_at: Option, - pub active_attempt_id: Option, - pub description: Option, - pub statement_descriptor: Option, - pub order_details: Option>, - pub attempt_count: Option, - pub frm_merchant_decision: Option, - pub payment_confirm_source: Option, - pub updated_by: String, - pub surcharge_applicable: Option, - pub authorization_count: Option, - pub session_expiry: Option, - pub request_external_three_ds_authentication: Option, - pub frm_metadata: Option, - pub customer_details: Option>>, - pub billing_address: Option>>, - pub shipping_address: Option>>, - pub merchant_order_reference_id: Option, - pub is_payment_processor_token_flow: Option, + merchant_reference_id: Option, + routing_algorithm_id: Option, + capture_method: Option, + authentication_type: Option, + billing_address: Option>>, + shipping_address: Option>>, + customer_id: Option, + customer_present: Option, + description: Option, + return_url: Option, + setup_future_usage: Option, + apply_mit_exemption: Option, + // TODO: Check if it is supported + // payment_method_token: Option, + statement_descriptor: Option, + order_details: Option>>, + allowed_payment_method_types: Option, + metadata: Option, + connector_metadata: Option, + feature_metadata: Option, + payment_link_enabled: Option, + payment_link_config: Option, + request_incremental_authorization: Option, + session_expiry: Option, + frm_metadata: Option, + request_external_three_ds_authentication: Option, + + // updated_by is set internally, field not present in request + updated_by: String, + }, } #[cfg(feature = "v1")] @@ -379,48 +382,164 @@ impl From for diesel_models::PaymentIntentUpdateInternal { status: Some(status), active_attempt_id: Some(active_attempt_id), modified_at: common_utils::date_time::now(), + amount: None, + currency: None, + merchant_reference_id: None, + routing_algorithm_id: None, + capture_method: None, + authentication_type: None, + billing_address: None, + shipping_address: None, + customer_id: None, + customer_present: None, + description: None, + return_url: None, + setup_future_usage: None, + apply_mit_exemption: None, + statement_descriptor: None, + order_details: None, + allowed_payment_method_types: None, + metadata: None, + connector_metadata: None, + feature_metadata: None, + payment_link_enabled: None, + request_incremental_authorization: None, + session_expiry: None, + frm_metadata: None, + request_external_three_ds_authentication: None, updated_by, }, + PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self { status: Some(status), active_attempt_id: None, modified_at: common_utils::date_time::now(), + amount: None, + currency: None, + merchant_reference_id: None, + routing_algorithm_id: None, + capture_method: None, + authentication_type: None, + billing_address: None, + shipping_address: None, + customer_id: None, + customer_present: None, + description: None, + return_url: None, + setup_future_usage: None, + apply_mit_exemption: None, + statement_descriptor: None, + order_details: None, + allowed_payment_method_types: None, + metadata: None, + connector_metadata: None, + feature_metadata: None, + payment_link_enabled: None, + request_incremental_authorization: None, + session_expiry: None, + frm_metadata: None, + request_external_three_ds_authentication: None, updated_by, }, PaymentIntentUpdate::SyncUpdate { status, updated_by } => Self { status: Some(status), active_attempt_id: None, modified_at: common_utils::date_time::now(), + amount: None, + currency: None, + merchant_reference_id: None, + routing_algorithm_id: None, + capture_method: None, + authentication_type: None, + billing_address: None, + shipping_address: None, + customer_id: None, + customer_present: None, + description: None, + return_url: None, + setup_future_usage: None, + apply_mit_exemption: None, + statement_descriptor: None, + order_details: None, + allowed_payment_method_types: None, + metadata: None, + connector_metadata: None, + feature_metadata: None, + payment_link_enabled: None, + request_incremental_authorization: None, + session_expiry: None, + frm_metadata: None, + request_external_three_ds_authentication: None, updated_by, }, - } - } -} - -// This conversion is required for the `apply_changeset` function used for mockdb -#[cfg(feature = "v2")] -impl From for PaymentIntentUpdateInternal { - fn from(payment_intent_update: PaymentIntentUpdate) -> Self { - match payment_intent_update { - PaymentIntentUpdate::ConfirmIntent { - status, - active_attempt_id, + PaymentIntentUpdate::UpdateIntent { + amount, + currency, + merchant_reference_id, + routing_algorithm_id, + capture_method, + authentication_type, + billing_address, + shipping_address, + customer_id, + customer_present, + description, + return_url, + setup_future_usage, + apply_mit_exemption, + statement_descriptor, + order_details, + allowed_payment_method_types, + metadata, + connector_metadata, + feature_metadata, + payment_link_enabled, + payment_link_config, + request_incremental_authorization, + session_expiry, + frm_metadata, + request_external_three_ds_authentication, updated_by, } => Self { - status: Some(status), - active_attempt_id: Some(active_attempt_id), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self { - status: Some(status), - updated_by, - ..Default::default() - }, - PaymentIntentUpdate::SyncUpdate { status, updated_by } => Self { - status: Some(status), + status: None, + active_attempt_id: None, + modified_at: common_utils::date_time::now(), + + amount, + currency, + merchant_reference_id, + routing_algorithm_id, + capture_method, + authentication_type, + billing_address: billing_address.map(Encryption::from), + shipping_address: shipping_address.map(Encryption::from), + customer_id, + customer_present: customer_present.map(|val| val.as_bool()), + description, + return_url, + setup_future_usage, + apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), + statement_descriptor, + order_details: order_details + .map(|order_details| { + order_details + .into_iter() + .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) + .collect::, _>>() + }) + .and_then(|r| r.ok()), + allowed_payment_method_types, + metadata, + connector_metadata, + feature_metadata, + payment_link_enabled: payment_link_enabled.map(|val| val.as_bool()), + request_incremental_authorization, + session_expiry, + frm_metadata, + request_external_three_ds_authentication: request_external_three_ds_authentication + .map(|val| val.as_bool()), + updated_by, - ..Default::default() }, } } @@ -623,6 +742,7 @@ impl From for PaymentIntentUpdateInternal { } } +#[cfg(feature = "v1")] use diesel_models::{ PaymentIntentUpdate as DieselPaymentIntentUpdate, PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields, @@ -814,75 +934,6 @@ impl From for DieselPaymentIntentUpdate { } } -// TODO: evaluate if we will be using the same update struct for v2 as well, uncomment this and make necessary changes if necessary -#[cfg(feature = "v2")] -impl From for diesel_models::PaymentIntentUpdateInternal { - fn from(value: PaymentIntentUpdateInternal) -> Self { - todo!() - // let modified_at = common_utils::date_time::now(); - // let PaymentIntentUpdateInternal { - // amount, - // currency, - // status, - // amount_captured, - // customer_id, - // return_url, - // setup_future_usage, - // off_session, - // metadata, - // modified_at: _, - // active_attempt_id, - // description, - // statement_descriptor, - // order_details, - // attempt_count, - // frm_merchant_decision, - // payment_confirm_source, - // updated_by, - // surcharge_applicable, - // authorization_count, - // session_expiry, - // request_external_three_ds_authentication, - // frm_metadata, - // customer_details, - // billing_address, - // merchant_order_reference_id, - // shipping_address, - // is_payment_processor_token_flow, - // } = value; - // Self { - // amount, - // currency, - // status, - // amount_captured, - // customer_id, - // return_url, - // setup_future_usage, - // off_session, - // metadata, - // modified_at, - // active_attempt_id, - // description, - // statement_descriptor, - // order_details, - // attempt_count, - // frm_merchant_decision, - // payment_confirm_source, - // updated_by, - // surcharge_applicable, - // authorization_count, - // session_expiry, - // request_external_three_ds_authentication, - // frm_metadata, - // customer_details: customer_details.map(Encryption::from), - // billing_address: billing_address.map(Encryption::from), - // merchant_order_reference_id, - // shipping_address: shipping_address.map(Encryption::from), - // is_payment_processor_token_flow, - // } - } -} - #[cfg(feature = "v1")] impl From for diesel_models::PaymentIntentUpdateInternal { fn from(value: PaymentIntentUpdateInternal) -> Self { @@ -1390,10 +1441,9 @@ impl behaviour::Conversion for PaymentIntent { .unwrap_or_default(), authorization_count: storage_model.authorization_count, session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: - common_enums::External3dsAuthenticationRequest::from( - storage_model.request_external_three_ds_authentication, - ), + request_external_three_ds_authentication: External3dsAuthenticationRequest::from( + storage_model.request_external_three_ds_authentication, + ), frm_metadata: storage_model.frm_metadata, customer_details: data.customer_details, billing_address, diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs b/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs index c43d72ce8def..bbe1a65de860 100644 --- a/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs +++ b/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs @@ -62,5 +62,8 @@ pub struct PaymentCreateIntent; #[derive(Debug, Clone)] pub struct PaymentGetIntent; +#[derive(Debug, Clone)] +pub struct PaymentUpdateIntent; + #[derive(Debug, Clone)] pub struct PostSessionTokens; diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 7c09813ca6b6..e2f85025046d 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -124,6 +124,7 @@ Never share your secret api keys. Keep them guarded and secure. //Routes for payments routes::payments::payments_create_intent, routes::payments::payments_get_intent, + routes::payments::payments_update_intent, routes::payments::payments_confirm_intent, routes::payments::payment_status, @@ -328,6 +329,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentsSessionRequest, api_models::payments::PaymentsSessionResponse, api_models::payments::PaymentsCreateIntentRequest, + api_models::payments::PaymentsUpdateIntentRequest, api_models::payments::PaymentsIntentResponse, api_models::payments::PazeWalletData, api_models::payments::AmountDetails, diff --git a/crates/openapi/src/routes/payments.rs b/crates/openapi/src/routes/payments.rs index 2ffd4f4cdc79..98bf01900088 100644 --- a/crates/openapi/src/routes/payments.rs +++ b/crates/openapi/src/routes/payments.rs @@ -652,6 +652,37 @@ pub fn payments_create_intent() {} )] #[cfg(feature = "v2")] pub fn payments_get_intent() {} + +/// Payments - Update Intent +/// +/// **Update a payment intent object** +/// +/// You will require the 'API - Key' from the Hyperswitch dashboard to make the call. +#[utoipa::path( + post, + path = "/v2/payments/{id}/update-intent", + params (("id" = String, Path, description = "The unique identifier for the Payment Intent")), + request_body( + content = PaymentsUpdateIntentRequest, + examples( + ( + "Update a payment intent with minimal fields" = ( + value = json!({"amount_details": {"order_amount": 6540, "currency": "USD"}}) + ) + ), + ), + ), + responses( + (status = 200, description = "Payment Intent Updated", body = PaymentsIntentResponse), + (status = 404, description = "Payment Intent Not Found") + ), + tag = "Payments", + operation_id = "Update a Payment Intent", + security(("api_key" = [])), +)] +#[cfg(feature = "v2")] +pub fn payments_update_intent() {} + /// Payments - Confirm Intent /// /// **Confirms a payment intent object with the payment method data** diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 23cb25a52d5c..cc0256c0fe8b 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -37,7 +37,7 @@ use futures::future::join_all; use helpers::{decrypt_paze_token, ApplePayData}; #[cfg(feature = "v2")] use hyperswitch_domain_models::payments::{ - PaymentConfirmData, PaymentIntentData, PaymentStatusData, + PaymentConfirmData, PaymentIntentData, PaymentStatusData, PaymentUpdateData, }; #[cfg(feature = "v2")] use hyperswitch_domain_models::router_response_types::RedirectForm; @@ -1011,12 +1011,13 @@ where #[instrument(skip_all, fields(payment_id, merchant_id))] pub async fn payments_intent_operation_core( state: &SessionState, - _req_state: ReqState, + req_state: ReqState, merchant_account: domain::MerchantAccount, profile: domain::Profile, key_store: domain::MerchantKeyStore, operation: Op, req: Req, + payment_id: Option, header_payload: HeaderPayload, ) -> RouterResult<(D, Req, Option)> where @@ -1033,7 +1034,11 @@ where .to_validate_request()? .validate_request(&req, &merchant_account)?; - let payment_id = id_type::GlobalPaymentId::generate(state.conf.cell_information.id.clone()); + // If payment_id is not present in request, generate new payment_id + let payment_id = match payment_id { + Some(id) => id, + None => id_type::GlobalPaymentId::generate(state.conf.cell_information.id.clone()), + }; tracing::Span::current().record("global_payment_id", payment_id.get_string_repr()); @@ -1064,6 +1069,22 @@ where .await .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) .attach_printable("Failed while fetching/creating customer")?; + + let (_operation, payment_data) = operation + .to_update_tracker()? + .update_trackers( + state, + req_state, + payment_data, + customer.clone(), + merchant_account.storage_scheme, + None, //TODO: fix this + &key_store, + None, + header_payload, + ) + .await?; + Ok((payment_data, req, customer)) } @@ -1449,6 +1470,7 @@ pub async fn payments_intent_core( key_store: domain::MerchantKeyStore, operation: Op, req: Req, + payment_id: Option, header_payload: HeaderPayload, ) -> RouterResponse where @@ -1466,6 +1488,7 @@ where key_store, operation.clone(), req, + payment_id, header_payload.clone(), ) .await?; @@ -7291,3 +7314,217 @@ impl OperationSessionSetters for PaymentStatusData { todo!() } } + +#[cfg(feature = "v2")] +impl OperationSessionGetters for PaymentUpdateData { + fn get_payment_attempt(&self) -> &storage::PaymentAttempt { + todo!() + } + + fn get_payment_intent(&self) -> &storage::PaymentIntent { + &self.payment_intent + } + + fn get_payment_method_info(&self) -> Option<&domain::PaymentMethod> { + todo!() + } + + fn get_mandate_id(&self) -> Option<&payments_api::MandateIds> { + todo!() + } + + // what is this address find out and not required remove this + fn get_address(&self) -> &PaymentAddress { + todo!() + } + + fn get_creds_identifier(&self) -> Option<&str> { + todo!() + } + + fn get_token(&self) -> Option<&str> { + todo!() + } + + fn get_multiple_capture_data(&self) -> Option<&types::MultipleCaptureData> { + todo!() + } + + fn get_payment_link_data(&self) -> Option { + todo!() + } + + fn get_ephemeral_key(&self) -> Option { + todo!() + } + + fn get_setup_mandate(&self) -> Option<&MandateData> { + todo!() + } + + fn get_poll_config(&self) -> Option { + todo!() + } + + fn get_authentication(&self) -> Option<&storage::Authentication> { + todo!() + } + + fn get_frm_message(&self) -> Option { + todo!() + } + + fn get_refunds(&self) -> Vec { + todo!() + } + + fn get_disputes(&self) -> Vec { + todo!() + } + + fn get_authorizations(&self) -> Vec { + todo!() + } + + fn get_attempts(&self) -> Option> { + todo!() + } + + fn get_recurring_details(&self) -> Option<&RecurringDetails> { + todo!() + } + + fn get_payment_intent_profile_id(&self) -> Option<&id_type::ProfileId> { + Some(&self.payment_intent.profile_id) + } + + fn get_currency(&self) -> storage_enums::Currency { + self.payment_intent.amount_details.currency + } + + fn get_amount(&self) -> api::Amount { + todo!() + } + + fn get_payment_attempt_connector(&self) -> Option<&str> { + todo!() + } + + fn get_billing_address(&self) -> Option { + todo!() + } + + fn get_payment_method_data(&self) -> Option<&domain::PaymentMethodData> { + todo!() + } + + fn get_sessions_token(&self) -> Vec { + todo!() + } + + fn get_token_data(&self) -> Option<&storage::PaymentTokenData> { + todo!() + } + + fn get_mandate_connector(&self) -> Option<&MandateConnectorDetails> { + todo!() + } + + fn get_force_sync(&self) -> Option { + todo!() + } + + fn get_capture_method(&self) -> Option { + todo!() + } + + #[cfg(feature = "v2")] + fn get_optional_payment_attempt(&self) -> Option<&storage::PaymentAttempt> { + todo!(); + } +} + +#[cfg(feature = "v2")] +impl OperationSessionSetters for PaymentUpdateData { + // Setters Implementation + fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent) { + self.payment_intent = payment_intent; + } + + fn set_payment_attempt(&mut self, _payment_attempt: storage::PaymentAttempt) { + todo!() + } + + fn set_payment_method_data(&mut self, _payment_method_data: Option) { + todo!() + } + + fn set_payment_method_id_in_attempt(&mut self, _payment_method_id: Option) { + todo!() + } + + fn set_email_if_not_present(&mut self, _email: pii::Email) { + todo!() + } + + fn set_pm_token(&mut self, _token: String) { + todo!() + } + + fn set_connector_customer_id(&mut self, _customer_id: Option) { + todo!() + } + + fn push_sessions_token(&mut self, _token: api::SessionToken) { + todo!() + } + + fn set_surcharge_details(&mut self, _surcharge_details: Option) { + todo!() + } + + fn set_merchant_connector_id_in_attempt( + &mut self, + merchant_connector_id: Option, + ) { + todo!() + } + + fn set_frm_message(&mut self, _frm_message: FraudCheck) { + todo!() + } + + fn set_payment_intent_status(&mut self, status: storage_enums::IntentStatus) { + self.payment_intent.status = status; + } + + fn set_authentication_type_in_attempt( + &mut self, + _authentication_type: Option, + ) { + todo!() + } + + fn set_recurring_mandate_payment_data( + &mut self, + _recurring_mandate_payment_data: + hyperswitch_domain_models::router_data::RecurringMandatePaymentData, + ) { + todo!() + } + + fn set_mandate_id(&mut self, _mandate_id: api_models::payments::MandateIds) { + todo!() + } + + fn set_setup_future_usage_in_payment_intent( + &mut self, + setup_future_usage: storage_enums::FutureUsage, + ) { + self.payment_intent.setup_future_usage = setup_future_usage; + } + + fn set_connector_in_payment_attempt(&mut self, _connector: Option) { + todo!() + } +} diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index edf0485a77df..97b8f31b492d 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -34,6 +34,8 @@ pub mod payment_confirm_intent; pub mod payment_create_intent; #[cfg(feature = "v2")] pub mod payment_get_intent; +#[cfg(feature = "v2")] +pub mod payment_update_intent; #[cfg(feature = "v2")] pub mod payment_get; @@ -54,6 +56,8 @@ pub use self::payment_get::PaymentGet; #[cfg(feature = "v2")] pub use self::payment_get_intent::PaymentGetIntent; pub use self::payment_response::PaymentResponse; +#[cfg(feature = "v2")] +pub use self::payment_update_intent::PaymentUpdateIntent; #[cfg(feature = "v1")] pub use self::{ payment_approve::PaymentApprove, payment_cancel::PaymentCancel, diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs new file mode 100644 index 000000000000..3bd8e9a49bc9 --- /dev/null +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -0,0 +1,353 @@ +use std::marker::PhantomData; + +use api_models::{enums::FrmSuggestion, payments::PaymentsUpdateIntentRequest}; +use async_trait::async_trait; +use common_utils::{ + errors::CustomResult, + ext_traits::{AsyncExt, ValueExt}, + types::MinorUnit, +}; +use error_stack::ResultExt; +use hyperswitch_domain_models::payments::{ + payment_intent::PaymentIntentUpdate, AmountDetails, PaymentIntent, +}; +use masking::Secret; +use router_env::{instrument, tracing}; + +use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; +use crate::{ + core::{ + errors::{self, RouterResult}, + payment_methods::cards::create_encrypted_data, + payments::{self, helpers, operations}, + }, + db::errors::StorageErrorExt, + routes::{app::ReqState, SessionState}, + types::{ + api, domain, + storage::{self, enums}, + }, +}; + +#[derive(Debug, Clone, Copy)] +pub struct PaymentUpdateIntent; + +impl Operation for &PaymentUpdateIntent { + type Data = payments::PaymentUpdateData; + fn to_validate_request( + &self, + ) -> RouterResult< + &(dyn ValidateRequest + Send + Sync), + > { + Ok(*self) + } + fn to_get_tracker( + &self, + ) -> RouterResult<&(dyn GetTracker + Send + Sync)> + { + Ok(*self) + } + fn to_domain(&self) -> RouterResult<&(dyn Domain)> { + Ok(*self) + } + fn to_update_tracker( + &self, + ) -> RouterResult<&(dyn UpdateTracker + Send + Sync)> + { + Ok(*self) + } +} + +impl Operation for PaymentUpdateIntent { + type Data = payments::PaymentUpdateData; + fn to_validate_request( + &self, + ) -> RouterResult< + &(dyn ValidateRequest + Send + Sync), + > { + Ok(self) + } + fn to_get_tracker( + &self, + ) -> RouterResult<&(dyn GetTracker + Send + Sync)> + { + Ok(self) + } + fn to_domain(&self) -> RouterResult<&dyn Domain> { + Ok(self) + } + fn to_update_tracker( + &self, + ) -> RouterResult<&(dyn UpdateTracker + Send + Sync)> + { + Ok(self) + } +} + +type PaymentsUpdateIntentOperation<'b, F> = + BoxedOperation<'b, F, PaymentsUpdateIntentRequest, payments::PaymentUpdateData>; + +#[async_trait] +impl GetTracker, PaymentsUpdateIntentRequest> + for PaymentUpdateIntent +{ + #[instrument(skip_all)] + async fn get_trackers<'a>( + &'a self, + state: &'a SessionState, + payment_id: &common_utils::id_type::GlobalPaymentId, + request: &PaymentsUpdateIntentRequest, + merchant_account: &domain::MerchantAccount, + profile: &domain::Profile, + key_store: &domain::MerchantKeyStore, + _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + ) -> RouterResult< + operations::GetTrackerResponse< + 'a, + F, + PaymentsUpdateIntentRequest, + payments::PaymentUpdateData, + >, + > { + 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, payment_id, key_store, storage_scheme) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + + // TODO: Use Batch Encryption + let billing_address = request + .billing + .clone() + .async_map(|billing_details| { + create_encrypted_data(key_manager_state, key_store, billing_details) + }) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt billing details")? + .map(|encrypted_value| { + encrypted_value.deserialize_inner_value(|value| value.parse_value("Address")) + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to deserialize decrypted value to Address")?; + + let shipping_address = request + .shipping + .clone() + .async_map(|shipping_details| { + create_encrypted_data(key_manager_state, key_store, shipping_details) + }) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt shipping details")? + .map(|encrypted_value| { + encrypted_value.deserialize_inner_value(|value| value.parse_value("Address")) + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to deserialize decrypted value to Address")?; + + let order_details = request + .order_details + .clone() + .map(|order_details| order_details.into_iter().map(Secret::new).collect()); + + // TODO: This should most likely be created_time + session_expiry rather than now + session_expiry + let session_expiry = request.session_expiry.map(|expiry| { + common_utils::date_time::now() + .saturating_add(time::Duration::seconds(i64::from(expiry))) + }); + + let payment_intent_update = PaymentIntentUpdate::UpdateIntent { + amount: request + .amount_details + .as_ref() + .map(|details| MinorUnit::from(details.order_amount())), + currency: request + .amount_details + .as_ref() + .map(|details| details.currency()), + merchant_reference_id: request.merchant_reference_id.clone(), + routing_algorithm_id: request.routing_algorithm_id.clone(), + capture_method: request.capture_method.clone(), + authentication_type: request.authentication_type.clone(), + billing_address, + shipping_address, + 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.clone(), + apply_mit_exemption: request.apply_mit_exemption.clone(), + statement_descriptor: request.statement_descriptor.clone(), + order_details, + 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.clone(), + session_expiry: session_expiry, + // TODO: Does frm_metadata need more processing? + frm_metadata: request.frm_metadata.clone(), + request_external_three_ds_authentication: request + .request_external_three_ds_authentication + .clone(), + updated_by: storage_scheme.to_string(), + }; + + let payment_data = payments::PaymentUpdateData { + flow: PhantomData, + payment_intent, + payment_intent_update, + }; + + let get_trackers_response = operations::GetTrackerResponse { + operation: Box::new(self), + payment_data, + }; + + Ok(get_trackers_response) + } +} + +#[async_trait] +impl UpdateTracker, PaymentsUpdateIntentRequest> + for PaymentUpdateIntent +{ + #[instrument(skip_all)] + async fn update_trackers<'b>( + &'b self, + state: &'b SessionState, + _req_state: ReqState, + payment_data: payments::PaymentUpdateData, + _customer: Option, + storage_scheme: enums::MerchantStorageScheme, + _updated_customer: Option, + key_store: &domain::MerchantKeyStore, + _frm_suggestion: Option, + _header_payload: hyperswitch_domain_models::payments::HeaderPayload, + ) -> RouterResult<( + PaymentsUpdateIntentOperation<'b, F>, + payments::PaymentUpdateData, + )> + where + F: 'b + Send, + { + let db = &*state.store; + let key_manager_state = &state.into(); + + let new_payment_intent = db + .update_payment_intent( + key_manager_state, + payment_data.payment_intent, + payment_data.payment_intent_update.clone(), + key_store, + storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Could not update Intent")?; + + let payment_data = payments::PaymentUpdateData { + flow: PhantomData, + payment_intent: new_payment_intent, + payment_intent_update: payment_data.payment_intent_update, + }; + + Ok((Box::new(self), payment_data)) + } +} + +impl + ValidateRequest> + for PaymentUpdateIntent +{ + #[instrument(skip_all)] + fn validate_request<'a, 'b>( + &'b self, + _request: &PaymentsUpdateIntentRequest, + merchant_account: &'a domain::MerchantAccount, + ) -> RouterResult<( + PaymentsUpdateIntentOperation<'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 Domain> + for PaymentUpdateIntent +{ + #[instrument(skip_all)] + async fn get_customer_details<'a>( + &'a self, + state: &SessionState, + payment_data: &mut payments::PaymentUpdateData, + merchant_key_store: &domain::MerchantKeyStore, + storage_scheme: enums::MerchantStorageScheme, + ) -> CustomResult< + ( + BoxedOperation<'a, F, PaymentsUpdateIntentRequest, payments::PaymentUpdateData>, + Option, + ), + 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::PaymentUpdateData, + _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, + _customer: &Option, + _business_profile: &domain::Profile, + ) -> RouterResult<( + PaymentsUpdateIntentOperation<'a, F>, + Option, + Option, + )> { + Ok((Box::new(self), None, None)) + } + + #[instrument(skip_all)] + async fn perform_routing<'a>( + &'a self, + merchant_account: &domain::MerchantAccount, + business_profile: &domain::Profile, + state: &SessionState, + // TODO: do not take the whole payment data here + payment_data: &mut payments::PaymentUpdateData, + mechant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + Ok(api::ConnectorCallType::Skip) + } + + #[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::PaymentUpdateData, + ) -> CustomResult { + Ok(false) + } +} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index d747f96b9fb9..57beec1dad94 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -532,6 +532,10 @@ impl Payments { web::resource("/get-intent") .route(web::get().to(payments::payments_get_intent)), ) + .service( + web::resource("/update-intent") + .route(web::post().to(payments::payments_update_intent)), + ) .service( web::resource("/create-external-sdk-tokens") .route(web::post().to(payments::payments_connector_session)), diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 4d3718b967dd..d462d05c0794 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -141,6 +141,7 @@ impl From for ApiIdentifier { | Flow::PaymentsCreateIntent | Flow::PaymentsGetIntent | Flow::PaymentsPostSessionTokens + | Flow::PaymentsUpdateIntent | Flow::PaymentStartRedirection => Self::Payments, Flow::PayoutsCreate diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index d674770f4e8b..95d390ca5498 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -138,6 +138,7 @@ pub async fn payments_create_intent( auth.key_store, payments::operations::PaymentIntentCreate, req, + None, header_payload.clone(), ) }, @@ -178,6 +179,8 @@ pub async fn payments_get_intent( id: path.into_inner(), }; + let global_payment_id = payload.id.clone(); + Box::pin(api::server_wrap( flow, state, @@ -198,6 +201,62 @@ pub async fn payments_get_intent( auth.key_store, payments::operations::PaymentGetIntent, req, + Some(global_payment_id.clone()), + header_payload.clone(), + ) + }, + &auth::HeaderAuth(auth::ApiKeyAuth), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "v2")] +#[instrument(skip_all, fields(flow = ?Flow::PaymentsUpdateIntent, payment_id))] +pub async fn payments_update_intent( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json, + path: web::Path, +) -> impl Responder { + use hyperswitch_domain_models::payments::PaymentUpdateData; + + let flow = Flow::PaymentsUpdateIntent; + let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { + Ok(headers) => headers, + Err(err) => { + return api::log_and_return_error_response(err); + } + }; + + let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId { + global_payment_id: path.into_inner(), + payload: json_payload.into_inner(), + }; + + let global_payment_id = internal_payload.global_payment_id.clone(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + internal_payload, + |state, auth: auth::AuthenticationDataV2, req, req_state| { + payments::payments_intent_core::< + api_types::PaymentUpdateIntent, + payment_types::PaymentsIntentResponse, + _, + _, + PaymentUpdateData, + >( + state, + req_state, + auth.merchant_account, + auth.profile, + auth.key_store, + payments::operations::PaymentUpdateIntent, + req.payload, + Some(global_payment_id.clone()), header_payload.clone(), ) }, diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index 57ef1d3336f5..f8b61ea05f64 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -19,13 +19,15 @@ pub use api_models::payments::{ VerifyResponse, WalletData, }; #[cfg(feature = "v2")] -pub use api_models::payments::{PaymentsCreateIntentRequest, PaymentsIntentResponse}; +pub use api_models::payments::{ + PaymentsCreateIntentRequest, PaymentsIntentResponse, PaymentsUpdateIntentRequest, +}; use error_stack::ResultExt; pub use hyperswitch_domain_models::router_flow_types::payments::{ Approve, Authorize, AuthorizeSessionToken, Balance, CalculateTax, Capture, CompleteAuthorize, CreateConnectorCustomer, IncrementalAuthorization, InitPayment, PSync, PaymentCreateIntent, - PaymentGetIntent, PaymentMethodToken, PostProcessing, PostSessionTokens, PreProcessing, Reject, - SdkSessionUpdate, Session, SetupMandate, Void, + PaymentGetIntent, PaymentMethodToken, PaymentUpdateIntent, PostProcessing, PostSessionTokens, + PreProcessing, Reject, SdkSessionUpdate, Session, SetupMandate, Void, }; pub use hyperswitch_interfaces::api::payments::{ ConnectorCustomer, MandateSetup, Payment, PaymentApprove, PaymentAuthorize, diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 5d59e7ddbac4..6c31b500626f 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -173,6 +173,8 @@ pub enum Flow { PaymentsCreateIntent, /// Payments Get Intent flow PaymentsGetIntent, + /// Payments Update Intent flow + PaymentsUpdateIntent, #[cfg(feature = "payouts")] /// Payouts create flow PayoutsCreate, diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index f26cf876ade8..305c63772acc 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -10,6 +10,8 @@ use common_utils::{ }; #[cfg(feature = "olap")] use diesel::{associations::HasTable, ExpressionMethods, JoinOnDsl, QueryDsl}; +#[cfg(feature = "v1")] +use diesel_models::payment_intent::PaymentIntentUpdate as DieselPaymentIntentUpdate; #[cfg(feature = "olap")] use diesel_models::query::generics::db_metrics; #[cfg(all(feature = "v1", feature = "olap"))] @@ -23,11 +25,7 @@ use diesel_models::schema_v2::{ payment_intent::dsl as pi_dsl, }; use diesel_models::{ - enums::MerchantStorageScheme, - kv, - payment_intent::{ - PaymentIntent as DieselPaymentIntent, PaymentIntentUpdate as DieselPaymentIntentUpdate, - }, + enums::MerchantStorageScheme, kv, payment_intent::PaymentIntent as DieselPaymentIntent, }; use error_stack::ResultExt; #[cfg(feature = "olap")] From d88cfa5bbfdf4bddd80a07273a2af5a0850815fe Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 6 Nov 2024 17:15:14 +0530 Subject: [PATCH 02/26] Fixed clippy --- .../src/payments/payment_intent.rs | 38 ++++++------- .../operations/payment_update_intent.rs | 56 +++++++++---------- 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 6b876880fe5a..9476cfd18c4c 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -297,31 +297,32 @@ pub enum PaymentIntentUpdate { routing_algorithm_id: Option, capture_method: Option, authentication_type: Option, - billing_address: Option>>, - shipping_address: Option>>, + billing_address: Box>>>, + shipping_address: Box>>>, customer_id: Option, customer_present: Option, description: Option, - return_url: Option, + return_url: Box>, setup_future_usage: Option, apply_mit_exemption: Option, // TODO: Check if it is supported // payment_method_token: Option, statement_descriptor: Option, - order_details: Option>>, - allowed_payment_method_types: Option, - metadata: Option, - connector_metadata: Option, - feature_metadata: Option, + order_details: Box>>>, + allowed_payment_method_types: Box>, + metadata: Box>, + connector_metadata: Box>, + feature_metadata: Box>, payment_link_enabled: Option, - payment_link_config: Option, + // TODO: check how to use this + // payment_link_config: Box>, request_incremental_authorization: Option, session_expiry: Option, - frm_metadata: Option, + frm_metadata: Box>, request_external_three_ds_authentication: Option, // updated_by is set internally, field not present in request - updated_by: String, + updated_by: Box, }, } @@ -494,7 +495,6 @@ impl From for diesel_models::PaymentIntentUpdateInternal { connector_metadata, feature_metadata, payment_link_enabled, - payment_link_config, request_incremental_authorization, session_expiry, frm_metadata, @@ -516,7 +516,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { customer_id, customer_present: customer_present.map(|val| val.as_bool()), description, - return_url, + return_url: *return_url, setup_future_usage, apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), statement_descriptor, @@ -528,18 +528,18 @@ impl From for diesel_models::PaymentIntentUpdateInternal { .collect::, _>>() }) .and_then(|r| r.ok()), - allowed_payment_method_types, - metadata, - connector_metadata, - feature_metadata, + allowed_payment_method_types: *allowed_payment_method_types, + metadata: *metadata, + connector_metadata: *connector_metadata, + feature_metadata: *feature_metadata, payment_link_enabled: payment_link_enabled.map(|val| val.as_bool()), request_incremental_authorization, session_expiry, - frm_metadata, + frm_metadata: *frm_metadata, request_external_three_ds_authentication: request_external_three_ds_authentication .map(|val| val.as_bool()), - updated_by, + updated_by: *updated_by, }, } } diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 3bd8e9a49bc9..2ac55acafd0a 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -8,9 +8,7 @@ use common_utils::{ types::MinorUnit, }; use error_stack::ResultExt; -use hyperswitch_domain_models::payments::{ - payment_intent::PaymentIntentUpdate, AmountDetails, PaymentIntent, -}; +use hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdate; use masking::Secret; use router_env::{instrument, tracing}; @@ -19,7 +17,7 @@ use crate::{ core::{ errors::{self, RouterResult}, payment_methods::cards::create_encrypted_data, - payments::{self, helpers, operations}, + payments::{self, operations}, }, db::errors::StorageErrorExt, routes::{app::ReqState, SessionState}, @@ -174,32 +172,31 @@ impl GetTracker, PaymentsUpda .map(|details| details.currency()), merchant_reference_id: request.merchant_reference_id.clone(), routing_algorithm_id: request.routing_algorithm_id.clone(), - capture_method: request.capture_method.clone(), - authentication_type: request.authentication_type.clone(), - billing_address, - shipping_address, + capture_method: request.capture_method, + authentication_type: request.authentication_type, + billing_address: Box::new(billing_address), + shipping_address: Box::new(shipping_address), 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.clone(), + return_url: Box::new(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, - 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(), + order_details: Box::new(order_details), + allowed_payment_method_types: Box::new(request.allowed_payment_method_types.clone()), + metadata: Box::new(request.metadata.clone()), + connector_metadata: Box::new(request.connector_metadata.clone()), + feature_metadata: Box::new(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.clone(), - session_expiry: session_expiry, + request_incremental_authorization: request.request_incremental_authorization, + session_expiry, // TODO: Does frm_metadata need more processing? - frm_metadata: request.frm_metadata.clone(), + frm_metadata: Box::new(request.frm_metadata.clone()), request_external_three_ds_authentication: request .request_external_three_ds_authentication .clone(), - updated_by: storage_scheme.to_string(), + updated_by: Box::new(storage_scheme.to_string()), }; let payment_data = payments::PaymentUpdateData { @@ -296,10 +293,10 @@ impl Domain( &'a self, - state: &SessionState, - payment_data: &mut payments::PaymentUpdateData, - merchant_key_store: &domain::MerchantKeyStore, - storage_scheme: enums::MerchantStorageScheme, + _state: &SessionState, + _payment_data: &mut payments::PaymentUpdateData, + _merchant_key_store: &domain::MerchantKeyStore, + _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult< ( BoxedOperation<'a, F, PaymentsUpdateIntentRequest, payments::PaymentUpdateData>, @@ -330,12 +327,11 @@ impl Domain( &'a self, - merchant_account: &domain::MerchantAccount, - business_profile: &domain::Profile, - state: &SessionState, - // TODO: do not take the whole payment data here - payment_data: &mut payments::PaymentUpdateData, - mechant_key_store: &domain::MerchantKeyStore, + _merchant_account: &domain::MerchantAccount, + _business_profile: &domain::Profile, + _state: &SessionState, + _payment_data: &mut payments::PaymentUpdateData, + _mechant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { Ok(api::ConnectorCallType::Skip) } From 199be74e1b7abe3c2269b5692c6583f63b2cc439 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Thu, 7 Nov 2024 13:26:21 +0530 Subject: [PATCH 03/26] Added amount_details fields --- crates/diesel_models/src/payment_intent.rs | 5 ++ .../hyperswitch_domain_models/src/payments.rs | 1 + .../src/payments/payment_intent.rs | 61 ++++++++++++++++--- .../operations/payment_update_intent.rs | 26 +++++++- 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 0a89e3044110..9dd90bed1bf6 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -501,6 +501,11 @@ pub struct PaymentIntentUpdateInternal { pub modified_at: PrimitiveDateTime, pub amount: Option, pub currency: Option, + pub shipping_cost: Option, + pub skip_external_tax_calculation: Option, + pub surcharge_applicable: Option, + pub surcharge_amount: Option, + pub tax_on_surcharge: Option, pub merchant_reference_id: Option, pub routing_algorithm_id: Option, pub capture_method: Option, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 2deab5ad1f5b..980cae92c220 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -15,6 +15,7 @@ use diesel_models::payment_intent::TaxDetails; #[cfg(feature = "v2")] use error_stack::ResultExt; use masking::Secret; +#[cfg(feature = "v2")] use payment_intent::PaymentIntentUpdate; use router_derive::ToEncryption; use rustc_hash::FxHashMap; diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 9476cfd18c4c..14d26ae4312f 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,7 +1,12 @@ +#[cfg(feature = "v2")] use api_models::payments::{Address, OrderDetailsWithAmount}; -use common_enums::{self as storage_enums, External3dsAuthenticationRequest}; +#[cfg(feature = "v2")] +use common_enums::External3dsAuthenticationRequest; +use common_enums::{self as storage_enums}; #[cfg(feature = "v2")] use common_utils::ext_traits::{Encode, ValueExt}; +#[cfg(feature = "v2")] +use common_utils::types::StatementDescriptor; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, crypto::Encryptable, @@ -12,7 +17,7 @@ use common_utils::{ type_name, types::{ keymanager::{self, KeyManagerState, ToEncryptable}, - MinorUnit, StatementDescriptor, + MinorUnit, }, }; #[cfg(feature = "v2")] @@ -289,25 +294,30 @@ pub enum PaymentIntentUpdate { updated_by: String, }, UpdateIntent { - // TODO: Add more amount_details fields amount: Option, currency: Option, - + shipping_cost: Option, + // TODO: Check how to handle this + // tax_details: Option, + skip_external_tax_calculation: Option, + skip_surcharge_calculation: Option, + surcharge_amount: Option, + tax_on_surcharge: Option, merchant_reference_id: Option, routing_algorithm_id: Option, capture_method: Option, authentication_type: Option, billing_address: Box>>>, shipping_address: Box>>>, - customer_id: Option, + customer_id: Box>, customer_present: Option, - description: Option, + description: Box>, return_url: Box>, setup_future_usage: Option, apply_mit_exemption: Option, // TODO: Check if it is supported // payment_method_token: Option, - statement_descriptor: Option, + statement_descriptor: Box>, order_details: Box>>>, allowed_payment_method_types: Box>, metadata: Box>, @@ -385,6 +395,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { modified_at: common_utils::date_time::now(), amount: None, currency: None, + shipping_cost: None, + skip_external_tax_calculation: None, + surcharge_applicable: None, + surcharge_amount: None, + tax_on_surcharge: None, merchant_reference_id: None, routing_algorithm_id: None, capture_method: None, @@ -417,6 +432,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { modified_at: common_utils::date_time::now(), amount: None, currency: None, + shipping_cost: None, + skip_external_tax_calculation: None, + surcharge_applicable: None, + surcharge_amount: None, + tax_on_surcharge: None, merchant_reference_id: None, routing_algorithm_id: None, capture_method: None, @@ -448,6 +468,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { modified_at: common_utils::date_time::now(), amount: None, currency: None, + shipping_cost: None, + skip_external_tax_calculation: None, + surcharge_applicable: None, + surcharge_amount: None, + tax_on_surcharge: None, merchant_reference_id: None, routing_algorithm_id: None, capture_method: None, @@ -476,6 +501,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { PaymentIntentUpdate::UpdateIntent { amount, currency, + shipping_cost, + skip_external_tax_calculation, + skip_surcharge_calculation, + surcharge_amount, + tax_on_surcharge, merchant_reference_id, routing_algorithm_id, capture_method, @@ -507,19 +537,30 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount, currency, + shipping_cost, + skip_external_tax_calculation: skip_external_tax_calculation.map(|val| match val { + common_enums::TaxCalculationOverride::Skip => true, + common_enums::TaxCalculationOverride::Calculate => false, + }), + surcharge_applicable: skip_surcharge_calculation.map(|val| match val { + common_enums::SurchargeCalculationOverride::Skip => true, + common_enums::SurchargeCalculationOverride::Calculate => false, + }), + surcharge_amount, + tax_on_surcharge, merchant_reference_id, routing_algorithm_id, capture_method, authentication_type, billing_address: billing_address.map(Encryption::from), shipping_address: shipping_address.map(Encryption::from), - customer_id, + customer_id: *customer_id, customer_present: customer_present.map(|val| val.as_bool()), - description, + description: *description, return_url: *return_url, setup_future_usage, apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), - statement_descriptor, + statement_descriptor: *statement_descriptor, order_details: order_details .map(|order_details| { order_details diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 2ac55acafd0a..2e670878e410 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -170,19 +170,39 @@ impl GetTracker, PaymentsUpda .amount_details .as_ref() .map(|details| details.currency()), + shipping_cost: request + .amount_details + .as_ref() + .and_then(|details| details.shipping_cost()), + skip_external_tax_calculation: request + .amount_details + .as_ref() + .map(|details| details.skip_external_tax_calculation()), + skip_surcharge_calculation: request + .amount_details + .as_ref() + .map(|details| details.skip_surcharge_calculation()), + surcharge_amount: request + .amount_details + .as_ref() + .and_then(|details| details.surcharge_amount()), + tax_on_surcharge: request + .amount_details + .as_ref() + .and_then(|details| details.tax_on_surcharge()), 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_address: Box::new(billing_address), shipping_address: Box::new(shipping_address), - customer_id: request.customer_id.clone(), + customer_id: Box::new(request.customer_id.clone()), customer_present: request.customer_present.clone(), - description: request.description.clone(), + description: Box::new(request.description.clone()), return_url: Box::new(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(), + statement_descriptor: Box::new(request.statement_descriptor.clone()), order_details: Box::new(order_details), allowed_payment_method_types: Box::new(request.allowed_payment_method_types.clone()), metadata: Box::new(request.metadata.clone()), From 59a9d804d34413cec463ed1c7260dded8e6c3507 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Fri, 8 Nov 2024 13:34:55 +0530 Subject: [PATCH 04/26] Addressed review comments --- crates/api_models/src/payments.rs | 18 +------------ crates/diesel_models/src/payment_intent.rs | 6 +---- .../src/payments/payment_intent.rs | 27 +++++-------------- crates/openapi/src/routes/payments.rs | 2 +- .../operations/payment_update_intent.rs | 4 +-- crates/router/src/routes/app.rs | 2 +- 6 files changed, 12 insertions(+), 47 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index b158f7fb5301..6f146682d186 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -302,16 +302,6 @@ pub struct PaymentsGetIntentRequest { pub struct PaymentsUpdateIntentRequest { pub amount_details: Option, - /// Unique identifier for the payment. This ensures idempotency for multiple payments - /// that have been done by a single merchant. - #[schema( - value_type = Option, - min_length = 30, - max_length = 30, - example = "pay_mbabizu24mvu3mela5njyhpit4" - )] - pub merchant_reference_id: Option, - /// The routing algorithm id to be used for the payment #[schema(value_type = Option)] pub routing_algorithm_id: Option, @@ -330,10 +320,6 @@ pub struct PaymentsUpdateIntentRequest { #[schema(value_type = Option
)] pub shipping: Option
, - /// The identifier for the customer - #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, - /// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. #[schema(example = true, value_type = Option)] pub customer_present: Option, @@ -353,8 +339,6 @@ pub struct PaymentsUpdateIntentRequest { #[schema(value_type = Option)] pub apply_mit_exemption: Option, - // TODO: Verify this is required - // pub payment_method_token: Option, /// 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)] pub statement_descriptor: Option, @@ -386,7 +370,7 @@ pub struct PaymentsUpdateIntentRequest { /// Whether to generate the payment link for this payment or not (if applicable) #[schema(value_type = Option)] - pub payment_link_enabled: Option, + pub enable_payment_link: Option, /// Configure a custom payment link for the particular payment #[schema(value_type = Option)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 9dd90bed1bf6..9e8132c0124f 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -506,13 +506,11 @@ pub struct PaymentIntentUpdateInternal { pub surcharge_applicable: Option, pub surcharge_amount: Option, pub tax_on_surcharge: Option, - pub merchant_reference_id: Option, pub routing_algorithm_id: Option, pub capture_method: Option, pub authentication_type: Option, pub billing_address: Option, pub shipping_address: Option, - pub customer_id: Option, pub customer_present: Option, pub description: Option, pub return_url: Option, @@ -524,9 +522,7 @@ pub struct PaymentIntentUpdateInternal { pub metadata: Option, pub connector_metadata: Option, pub feature_metadata: Option, - // TODO: Should we rename the field instead? - #[diesel(column_name = enable_payment_link)] - pub payment_link_enabled: Option, + pub enable_payment_link: Option, // TODO: Check this type // pub payment_link_config: Option, pub request_incremental_authorization: Option, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 14d26ae4312f..1933ff378f4c 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -293,6 +293,7 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, + /// UpdateIntent UpdateIntent { amount: Option, currency: Option, @@ -303,27 +304,23 @@ pub enum PaymentIntentUpdate { skip_surcharge_calculation: Option, surcharge_amount: Option, tax_on_surcharge: Option, - merchant_reference_id: Option, routing_algorithm_id: Option, capture_method: Option, authentication_type: Option, billing_address: Box>>>, shipping_address: Box>>>, - customer_id: Box>, customer_present: Option, description: Box>, return_url: Box>, setup_future_usage: Option, apply_mit_exemption: Option, - // TODO: Check if it is supported - // payment_method_token: Option, statement_descriptor: Box>, order_details: Box>>>, allowed_payment_method_types: Box>, metadata: Box>, connector_metadata: Box>, feature_metadata: Box>, - payment_link_enabled: Option, + enable_payment_link: Option, // TODO: check how to use this // payment_link_config: Box>, request_incremental_authorization: Option, @@ -400,13 +397,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { surcharge_applicable: None, surcharge_amount: None, tax_on_surcharge: None, - merchant_reference_id: None, routing_algorithm_id: None, capture_method: None, authentication_type: None, billing_address: None, shipping_address: None, - customer_id: None, customer_present: None, description: None, return_url: None, @@ -418,7 +413,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: None, connector_metadata: None, feature_metadata: None, - payment_link_enabled: None, + enable_payment_link: None, request_incremental_authorization: None, session_expiry: None, frm_metadata: None, @@ -437,13 +432,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { surcharge_applicable: None, surcharge_amount: None, tax_on_surcharge: None, - merchant_reference_id: None, routing_algorithm_id: None, capture_method: None, authentication_type: None, billing_address: None, shipping_address: None, - customer_id: None, customer_present: None, description: None, return_url: None, @@ -455,7 +448,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: None, connector_metadata: None, feature_metadata: None, - payment_link_enabled: None, + enable_payment_link: None, request_incremental_authorization: None, session_expiry: None, frm_metadata: None, @@ -473,13 +466,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { surcharge_applicable: None, surcharge_amount: None, tax_on_surcharge: None, - merchant_reference_id: None, routing_algorithm_id: None, capture_method: None, authentication_type: None, billing_address: None, shipping_address: None, - customer_id: None, customer_present: None, description: None, return_url: None, @@ -491,7 +482,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: None, connector_metadata: None, feature_metadata: None, - payment_link_enabled: None, + enable_payment_link: None, request_incremental_authorization: None, session_expiry: None, frm_metadata: None, @@ -506,13 +497,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { skip_surcharge_calculation, surcharge_amount, tax_on_surcharge, - merchant_reference_id, routing_algorithm_id, capture_method, authentication_type, billing_address, shipping_address, - customer_id, customer_present, description, return_url, @@ -524,7 +513,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata, connector_metadata, feature_metadata, - payment_link_enabled, + enable_payment_link, request_incremental_authorization, session_expiry, frm_metadata, @@ -548,13 +537,11 @@ impl From for diesel_models::PaymentIntentUpdateInternal { }), surcharge_amount, tax_on_surcharge, - merchant_reference_id, routing_algorithm_id, capture_method, authentication_type, billing_address: billing_address.map(Encryption::from), shipping_address: shipping_address.map(Encryption::from), - customer_id: *customer_id, customer_present: customer_present.map(|val| val.as_bool()), description: *description, return_url: *return_url, @@ -573,7 +560,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: *metadata, connector_metadata: *connector_metadata, feature_metadata: *feature_metadata, - payment_link_enabled: payment_link_enabled.map(|val| val.as_bool()), + enable_payment_link: enable_payment_link.map(|val| val.as_bool()), request_incremental_authorization, session_expiry, frm_metadata: *frm_metadata, diff --git a/crates/openapi/src/routes/payments.rs b/crates/openapi/src/routes/payments.rs index 98bf01900088..8b8ec1aec326 100644 --- a/crates/openapi/src/routes/payments.rs +++ b/crates/openapi/src/routes/payments.rs @@ -659,7 +659,7 @@ pub fn payments_get_intent() {} /// /// You will require the 'API - Key' from the Hyperswitch dashboard to make the call. #[utoipa::path( - post, + put, path = "/v2/payments/{id}/update-intent", params (("id" = String, Path, description = "The unique identifier for the Payment Intent")), request_body( diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 2e670878e410..d8647edb8d19 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -190,13 +190,11 @@ impl GetTracker, PaymentsUpda .amount_details .as_ref() .and_then(|details| details.tax_on_surcharge()), - 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_address: Box::new(billing_address), shipping_address: Box::new(shipping_address), - customer_id: Box::new(request.customer_id.clone()), customer_present: request.customer_present.clone(), description: Box::new(request.description.clone()), return_url: Box::new(request.return_url.clone()), @@ -208,7 +206,7 @@ impl GetTracker, PaymentsUpda metadata: Box::new(request.metadata.clone()), connector_metadata: Box::new(request.connector_metadata.clone()), feature_metadata: Box::new(request.feature_metadata.clone()), - payment_link_enabled: request.payment_link_enabled.clone(), + enable_payment_link: request.enable_payment_link.clone(), request_incremental_authorization: request.request_incremental_authorization, session_expiry, // TODO: Does frm_metadata need more processing? diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 57beec1dad94..552cb69bf9a7 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -534,7 +534,7 @@ impl Payments { ) .service( web::resource("/update-intent") - .route(web::post().to(payments::payments_update_intent)), + .route(web::put().to(payments::payments_update_intent)), ) .service( web::resource("/create-external-sdk-tokens") From a11abfa3a4865e77996ff2e4cefe0464edbe92db Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 13 Nov 2024 14:53:16 +0530 Subject: [PATCH 05/26] Address review comments --- api-reference-v2/openapi_spec.json | 20 +- crates/api_models/src/payments.rs | 4 +- crates/diesel_models/src/payment_intent.rs | 4 +- .../hyperswitch_domain_models/src/payments.rs | 25 +- .../src/payments/payment_intent.rs | 47 ++-- crates/router/src/core/payments.rs | 218 +---------------- .../operations/payment_update_intent.rs | 222 +++++++++++------- crates/router/src/routes/payments.rs | 6 +- 8 files changed, 188 insertions(+), 358 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index fdc6784c2745..ec6f59cfa09f 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1912,7 +1912,7 @@ } }, "/v2/payments/{id}/update-intent": { - "post": { + "put": { "tags": [ "Payments" ], @@ -15328,14 +15328,6 @@ ], "nullable": true }, - "merchant_reference_id": { - "type": "string", - "description": "Unique identifier for the payment. This ensures idempotency for multiple payments\nthat have been done by a single merchant.", - "example": "pay_mbabizu24mvu3mela5njyhpit4", - "nullable": true, - "maxLength": 30, - "minLength": 30 - }, "routing_algorithm_id": { "type": "string", "description": "The routing algorithm id to be used for the payment", @@ -15374,14 +15366,6 @@ ], "nullable": true }, - "customer_id": { - "type": "string", - "description": "The identifier for the customer", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", - "nullable": true, - "maxLength": 64, - "minLength": 1 - }, "customer_present": { "allOf": [ { @@ -15463,7 +15447,7 @@ ], "nullable": true }, - "payment_link_enabled": { + "enable_payment_link": { "allOf": [ { "$ref": "#/components/schemas/EnablePaymentLinkRequest" diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 6f146682d186..842c41182bcc 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -354,7 +354,7 @@ pub struct PaymentsUpdateIntentRequest { /// Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent #[schema(value_type = Option>)] - pub allowed_payment_method_types: Option, + pub allowed_payment_method_types: Option>, /// Metadata is useful for storing additional, unstructured information on an object. #[schema(value_type = Option, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)] @@ -366,7 +366,7 @@ pub struct PaymentsUpdateIntentRequest { /// Additional data that might be required by hyperswitch based on the requested features by the merchants. #[schema(value_type = Option)] - pub feature_metadata: Option, + pub feature_metadata: Option, /// Whether to generate the payment link for this payment or not (if applicable) #[schema(value_type = Option)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 9e8132c0124f..3466551e3cb7 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -517,11 +517,11 @@ pub struct PaymentIntentUpdateInternal { pub setup_future_usage: Option, pub apply_mit_exemption: Option, pub statement_descriptor: Option, - pub order_details: Option>, + pub order_details: Option>>, pub allowed_payment_method_types: Option, pub metadata: Option, pub connector_metadata: Option, - pub feature_metadata: Option, + pub feature_metadata: Option, pub enable_payment_link: Option, // TODO: Check this type // pub payment_link_config: Option, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 980cae92c220..95e2dbfe65e0 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -255,6 +255,20 @@ impl AmountDetails { order_tax_amount, } } + + pub fn update_from_request(self, req: &api_models::payments::AmountDetails) -> Self { + Self { + order_amount: req.order_amount().into(), + currency: req.currency(), + shipping_cost: req.shipping_cost().or(self.shipping_cost), + tax_details: self.tax_details, + skip_external_tax_calculation: self.skip_external_tax_calculation, + skip_surcharge_calculation: self.skip_surcharge_calculation, + surcharge_amount: req.surcharge_amount().or(self.surcharge_amount), + tax_on_surcharge: req.tax_on_surcharge().or(self.tax_on_surcharge), + amount_captured: self.amount_captured, + } + } } #[cfg(feature = "v2")] @@ -557,14 +571,3 @@ where /// This will depend on the payment status and the force sync flag in the request pub should_sync_with_connector: bool, } - -#[cfg(feature = "v2")] -#[derive(Clone)] -pub struct PaymentUpdateData -where - F: Clone, -{ - pub flow: PhantomData, - pub payment_intent: PaymentIntent, - pub payment_intent_update: PaymentIntentUpdate, -} diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 1933ff378f4c..b57265a5e493 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,5 +1,5 @@ #[cfg(feature = "v2")] -use api_models::payments::{Address, OrderDetailsWithAmount}; +use api_models::payments::Address; #[cfg(feature = "v2")] use common_enums::External3dsAuthenticationRequest; use common_enums::{self as storage_enums}; @@ -21,7 +21,6 @@ use common_utils::{ }, }; #[cfg(feature = "v2")] -use diesel_models::types::OrderDetailsWithAmount; use diesel_models::{ PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; @@ -39,7 +38,7 @@ use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, - RemoteStorageObject, + ApiModelToDieselModelConvertor, RemoteStorageObject, }; #[async_trait::async_trait] @@ -300,8 +299,8 @@ pub enum PaymentIntentUpdate { shipping_cost: Option, // TODO: Check how to handle this // tax_details: Option, - skip_external_tax_calculation: Option, - skip_surcharge_calculation: Option, + skip_external_tax_calculation: Option, + skip_surcharge_calculation: Option, surcharge_amount: Option, tax_on_surcharge: Option, routing_algorithm_id: Option, @@ -315,11 +314,11 @@ pub enum PaymentIntentUpdate { setup_future_usage: Option, apply_mit_exemption: Option, statement_descriptor: Box>, - order_details: Box>>>, - allowed_payment_method_types: Box>, + order_details: Box>>>, + allowed_payment_method_types: Box>>, metadata: Box>, connector_metadata: Box>, - feature_metadata: Box>, + feature_metadata: Box>, enable_payment_link: Option, // TODO: check how to use this // payment_link_config: Box>, @@ -528,12 +527,12 @@ impl From for diesel_models::PaymentIntentUpdateInternal { currency, shipping_cost, skip_external_tax_calculation: skip_external_tax_calculation.map(|val| match val { - common_enums::TaxCalculationOverride::Skip => true, - common_enums::TaxCalculationOverride::Calculate => false, + super::TaxCalculationOverride::Skip => true, + super::TaxCalculationOverride::Calculate => false, }), surcharge_applicable: skip_surcharge_calculation.map(|val| match val { - common_enums::SurchargeCalculationOverride::Skip => true, - common_enums::SurchargeCalculationOverride::Calculate => false, + super::SurchargeCalculationOverride::Skip => true, + super::SurchargeCalculationOverride::Calculate => false, }), surcharge_amount, tax_on_surcharge, @@ -548,18 +547,26 @@ impl From for diesel_models::PaymentIntentUpdateInternal { setup_future_usage, apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), statement_descriptor: *statement_descriptor, - order_details: order_details - .map(|order_details| { - order_details - .into_iter() - .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) - .collect::, _>>() + order_details: *order_details, + // .map(|order_details| { + // order_details + // .into_iter() + // .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) + // .collect::, _>>() + // }) + // .and_then(|r| r.ok()), + allowed_payment_method_types: allowed_payment_method_types + .map(|allowed_payment_method_types| { + allowed_payment_method_types.encode_to_value() }) - .and_then(|r| r.ok()), - allowed_payment_method_types: *allowed_payment_method_types, + .and_then(|r| r.ok().map(Secret::new)), + // .transpose() + // .ok().flatten() + // .map(Secret::new), metadata: *metadata, connector_metadata: *connector_metadata, feature_metadata: *feature_metadata, + // .map(|val| diesel_models::types::FeatureMetadata::convert_from(val)), enable_payment_link: enable_payment_link.map(|val| val.as_bool()), request_incremental_authorization, session_expiry, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index cc0256c0fe8b..bad960f11951 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -37,7 +37,7 @@ use futures::future::join_all; use helpers::{decrypt_paze_token, ApplePayData}; #[cfg(feature = "v2")] use hyperswitch_domain_models::payments::{ - PaymentConfirmData, PaymentIntentData, PaymentStatusData, PaymentUpdateData, + PaymentConfirmData, PaymentIntentData, PaymentStatusData, }; #[cfg(feature = "v2")] use hyperswitch_domain_models::router_response_types::RedirectForm; @@ -1078,7 +1078,7 @@ where payment_data, customer.clone(), merchant_account.storage_scheme, - None, //TODO: fix this + None, &key_store, None, header_payload, @@ -7314,217 +7314,3 @@ impl OperationSessionSetters for PaymentStatusData { todo!() } } - -#[cfg(feature = "v2")] -impl OperationSessionGetters for PaymentUpdateData { - fn get_payment_attempt(&self) -> &storage::PaymentAttempt { - todo!() - } - - fn get_payment_intent(&self) -> &storage::PaymentIntent { - &self.payment_intent - } - - fn get_payment_method_info(&self) -> Option<&domain::PaymentMethod> { - todo!() - } - - fn get_mandate_id(&self) -> Option<&payments_api::MandateIds> { - todo!() - } - - // what is this address find out and not required remove this - fn get_address(&self) -> &PaymentAddress { - todo!() - } - - fn get_creds_identifier(&self) -> Option<&str> { - todo!() - } - - fn get_token(&self) -> Option<&str> { - todo!() - } - - fn get_multiple_capture_data(&self) -> Option<&types::MultipleCaptureData> { - todo!() - } - - fn get_payment_link_data(&self) -> Option { - todo!() - } - - fn get_ephemeral_key(&self) -> Option { - todo!() - } - - fn get_setup_mandate(&self) -> Option<&MandateData> { - todo!() - } - - fn get_poll_config(&self) -> Option { - todo!() - } - - fn get_authentication(&self) -> Option<&storage::Authentication> { - todo!() - } - - fn get_frm_message(&self) -> Option { - todo!() - } - - fn get_refunds(&self) -> Vec { - todo!() - } - - fn get_disputes(&self) -> Vec { - todo!() - } - - fn get_authorizations(&self) -> Vec { - todo!() - } - - fn get_attempts(&self) -> Option> { - todo!() - } - - fn get_recurring_details(&self) -> Option<&RecurringDetails> { - todo!() - } - - fn get_payment_intent_profile_id(&self) -> Option<&id_type::ProfileId> { - Some(&self.payment_intent.profile_id) - } - - fn get_currency(&self) -> storage_enums::Currency { - self.payment_intent.amount_details.currency - } - - fn get_amount(&self) -> api::Amount { - todo!() - } - - fn get_payment_attempt_connector(&self) -> Option<&str> { - todo!() - } - - fn get_billing_address(&self) -> Option { - todo!() - } - - fn get_payment_method_data(&self) -> Option<&domain::PaymentMethodData> { - todo!() - } - - fn get_sessions_token(&self) -> Vec { - todo!() - } - - fn get_token_data(&self) -> Option<&storage::PaymentTokenData> { - todo!() - } - - fn get_mandate_connector(&self) -> Option<&MandateConnectorDetails> { - todo!() - } - - fn get_force_sync(&self) -> Option { - todo!() - } - - fn get_capture_method(&self) -> Option { - todo!() - } - - #[cfg(feature = "v2")] - fn get_optional_payment_attempt(&self) -> Option<&storage::PaymentAttempt> { - todo!(); - } -} - -#[cfg(feature = "v2")] -impl OperationSessionSetters for PaymentUpdateData { - // Setters Implementation - fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent) { - self.payment_intent = payment_intent; - } - - fn set_payment_attempt(&mut self, _payment_attempt: storage::PaymentAttempt) { - todo!() - } - - fn set_payment_method_data(&mut self, _payment_method_data: Option) { - todo!() - } - - fn set_payment_method_id_in_attempt(&mut self, _payment_method_id: Option) { - todo!() - } - - fn set_email_if_not_present(&mut self, _email: pii::Email) { - todo!() - } - - fn set_pm_token(&mut self, _token: String) { - todo!() - } - - fn set_connector_customer_id(&mut self, _customer_id: Option) { - todo!() - } - - fn push_sessions_token(&mut self, _token: api::SessionToken) { - todo!() - } - - fn set_surcharge_details(&mut self, _surcharge_details: Option) { - todo!() - } - - fn set_merchant_connector_id_in_attempt( - &mut self, - merchant_connector_id: Option, - ) { - todo!() - } - - fn set_frm_message(&mut self, _frm_message: FraudCheck) { - todo!() - } - - fn set_payment_intent_status(&mut self, status: storage_enums::IntentStatus) { - self.payment_intent.status = status; - } - - fn set_authentication_type_in_attempt( - &mut self, - _authentication_type: Option, - ) { - todo!() - } - - fn set_recurring_mandate_payment_data( - &mut self, - _recurring_mandate_payment_data: - hyperswitch_domain_models::router_data::RecurringMandatePaymentData, - ) { - todo!() - } - - fn set_mandate_id(&mut self, _mandate_id: api_models::payments::MandateIds) { - todo!() - } - - fn set_setup_future_usage_in_payment_intent( - &mut self, - setup_future_usage: storage_enums::FutureUsage, - ) { - self.payment_intent.setup_future_usage = setup_future_usage; - } - - fn set_connector_in_payment_attempt(&mut self, _connector: Option) { - todo!() - } -} diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index d8647edb8d19..d75daf64649f 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -7,8 +7,11 @@ use common_utils::{ ext_traits::{AsyncExt, ValueExt}, types::MinorUnit, }; +use diesel_models::{types::FeatureMetadata, PaymentIntentNew}; use error_stack::ResultExt; -use hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdate; +use hyperswitch_domain_models::{ + payments::payment_intent::PaymentIntentUpdate, ApiModelToDieselModelConvertor, +}; use masking::Secret; use router_env::{instrument, tracing}; @@ -31,7 +34,7 @@ use crate::{ pub struct PaymentUpdateIntent; impl Operation for &PaymentUpdateIntent { - type Data = payments::PaymentUpdateData; + type Data = payments::PaymentIntentData; fn to_validate_request( &self, ) -> RouterResult< @@ -57,7 +60,7 @@ impl Operation for &PaymentUpda } impl Operation for PaymentUpdateIntent { - type Data = payments::PaymentUpdateData; + type Data = payments::PaymentIntentData; fn to_validate_request( &self, ) -> RouterResult< @@ -83,10 +86,10 @@ impl Operation for PaymentUpdat } type PaymentsUpdateIntentOperation<'b, F> = - BoxedOperation<'b, F, PaymentsUpdateIntentRequest, payments::PaymentUpdateData>; + BoxedOperation<'b, F, PaymentsUpdateIntentRequest, payments::PaymentIntentData>; #[async_trait] -impl GetTracker, PaymentsUpdateIntentRequest> +impl GetTracker, PaymentsUpdateIntentRequest> for PaymentUpdateIntent { #[instrument(skip_all)] @@ -104,7 +107,7 @@ impl GetTracker, PaymentsUpda 'a, F, PaymentsUpdateIntentRequest, - payments::PaymentUpdateData, + payments::PaymentIntentData, >, > { let db = &*state.store; @@ -115,9 +118,34 @@ impl GetTracker, PaymentsUpda .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + let PaymentsUpdateIntentRequest { + amount_details, + routing_algorithm_id, + capture_method, + authentication_type, + billing, + shipping, + customer_present, + description, + return_url, + setup_future_usage, + apply_mit_exemption, + statement_descriptor, + order_details, + allowed_payment_method_types, + metadata, + connector_metadata, + feature_metadata, + enable_payment_link, + payment_link_config, + request_incremental_authorization, + session_expiry, + frm_metadata, + request_external_three_ds_authentication, + } = request.clone(); + // TODO: Use Batch Encryption - let billing_address = request - .billing + let billing_address = billing .clone() .async_map(|billing_details| { create_encrypted_data(key_manager_state, key_store, billing_details) @@ -133,8 +161,7 @@ impl GetTracker, PaymentsUpda .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to deserialize decrypted value to Address")?; - let shipping_address = request - .shipping + let shipping_address = shipping .clone() .async_map(|shipping_details| { create_encrypted_data(key_manager_state, key_store, shipping_details) @@ -150,77 +177,63 @@ impl GetTracker, PaymentsUpda .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to deserialize decrypted value to Address")?; - let order_details = request - .order_details - .clone() - .map(|order_details| order_details.into_iter().map(Secret::new).collect()); + let order_details = order_details.clone().map(|order_details| { + order_details + .into_iter() + .map(|order_detail| { + Secret::new(diesel_models::types::OrderDetailsWithAmount::convert_from( + order_detail, + )) + }) + .collect() + }); - // TODO: This should most likely be created_time + session_expiry rather than now + session_expiry - let session_expiry = request.session_expiry.map(|expiry| { - common_utils::date_time::now() + let session_expiry = session_expiry.map(|expiry| { + payment_intent + .created_at .saturating_add(time::Duration::seconds(i64::from(expiry))) }); - let payment_intent_update = PaymentIntentUpdate::UpdateIntent { - amount: request - .amount_details - .as_ref() - .map(|details| MinorUnit::from(details.order_amount())), - currency: request - .amount_details - .as_ref() - .map(|details| details.currency()), - shipping_cost: request - .amount_details - .as_ref() - .and_then(|details| details.shipping_cost()), - skip_external_tax_calculation: request - .amount_details - .as_ref() - .map(|details| details.skip_external_tax_calculation()), - skip_surcharge_calculation: request - .amount_details - .as_ref() - .map(|details| details.skip_surcharge_calculation()), - surcharge_amount: request - .amount_details - .as_ref() - .and_then(|details| details.surcharge_amount()), - tax_on_surcharge: request - .amount_details - .as_ref() - .and_then(|details| details.tax_on_surcharge()), - routing_algorithm_id: request.routing_algorithm_id.clone(), - capture_method: request.capture_method, - authentication_type: request.authentication_type, - billing_address: Box::new(billing_address), - shipping_address: Box::new(shipping_address), - customer_present: request.customer_present.clone(), - description: Box::new(request.description.clone()), - return_url: Box::new(request.return_url.clone()), - setup_future_usage: request.setup_future_usage, - apply_mit_exemption: request.apply_mit_exemption.clone(), - statement_descriptor: Box::new(request.statement_descriptor.clone()), - order_details: Box::new(order_details), - allowed_payment_method_types: Box::new(request.allowed_payment_method_types.clone()), - metadata: Box::new(request.metadata.clone()), - connector_metadata: Box::new(request.connector_metadata.clone()), - feature_metadata: Box::new(request.feature_metadata.clone()), - enable_payment_link: request.enable_payment_link.clone(), - request_incremental_authorization: request.request_incremental_authorization, - session_expiry, - // TODO: Does frm_metadata need more processing? - frm_metadata: Box::new(request.frm_metadata.clone()), - request_external_three_ds_authentication: request - .request_external_three_ds_authentication - .clone(), - updated_by: Box::new(storage_scheme.to_string()), + let updated_amount_details = match amount_details { + Some(details) => payment_intent.amount_details.update_from_request(&details), + None => payment_intent.amount_details, }; - let payment_data = payments::PaymentUpdateData { + let payment_intent = hyperswitch_domain_models::payments::PaymentIntent { + amount_details: updated_amount_details, + description: description.or(payment_intent.description), + return_url: return_url.or(payment_intent.return_url), + metadata: metadata.or(payment_intent.metadata), + statement_descriptor: statement_descriptor.or(payment_intent.statement_descriptor), + modified_at: common_utils::date_time::now(), + order_details, + connector_metadata: connector_metadata.or(payment_intent.connector_metadata), + feature_metadata: (feature_metadata + .map(FeatureMetadata::convert_from) + .or(payment_intent.feature_metadata)), + updated_by: storage_scheme.to_string(), + request_incremental_authorization: request_incremental_authorization + .unwrap_or(payment_intent.request_incremental_authorization), + session_expiry: session_expiry.unwrap_or(payment_intent.session_expiry), + request_external_three_ds_authentication: request_external_three_ds_authentication + .unwrap_or(payment_intent.request_external_three_ds_authentication), + frm_metadata: frm_metadata.or(payment_intent.frm_metadata), + billing_address, + shipping_address, + capture_method: capture_method.unwrap_or(payment_intent.capture_method), + authentication_type: authentication_type.unwrap_or(payment_intent.authentication_type), + enable_payment_link: enable_payment_link.unwrap_or(payment_intent.enable_payment_link), + apply_mit_exemption: apply_mit_exemption.unwrap_or(payment_intent.apply_mit_exemption), + customer_present: customer_present.unwrap_or(payment_intent.customer_present), + routing_algorithm_id: routing_algorithm_id.or(payment_intent.routing_algorithm_id), + allowed_payment_method_types: allowed_payment_method_types + .or(payment_intent.allowed_payment_method_types), + ..payment_intent + }; + + let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, - payment_intent_update, }; let get_trackers_response = operations::GetTrackerResponse { @@ -233,7 +246,7 @@ impl GetTracker, PaymentsUpda } #[async_trait] -impl UpdateTracker, PaymentsUpdateIntentRequest> +impl UpdateTracker, PaymentsUpdateIntentRequest> for PaymentUpdateIntent { #[instrument(skip_all)] @@ -241,7 +254,7 @@ impl UpdateTracker, PaymentsUpdateIn &'b self, state: &'b SessionState, _req_state: ReqState, - payment_data: payments::PaymentUpdateData, + payment_data: payments::PaymentIntentData, _customer: Option, storage_scheme: enums::MerchantStorageScheme, _updated_customer: Option, @@ -250,7 +263,7 @@ impl UpdateTracker, PaymentsUpdateIn _header_payload: hyperswitch_domain_models::payments::HeaderPayload, ) -> RouterResult<( PaymentsUpdateIntentOperation<'b, F>, - payments::PaymentUpdateData, + payments::PaymentIntentData, )> where F: 'b + Send, @@ -258,11 +271,49 @@ impl UpdateTracker, PaymentsUpdateIn let db = &*state.store; let key_manager_state = &state.into(); + let intent = payment_data.payment_intent.clone(); + + let payment_intent_update = PaymentIntentUpdate::UpdateIntent { + amount: Some(intent.amount_details.order_amount), + currency: Some(intent.amount_details.currency), + shipping_cost: intent.amount_details.shipping_cost, + skip_external_tax_calculation: Some( + intent.amount_details.skip_external_tax_calculation, + ), + skip_surcharge_calculation: Some(intent.amount_details.skip_surcharge_calculation), + surcharge_amount: intent.amount_details.surcharge_amount, + tax_on_surcharge: intent.amount_details.tax_on_surcharge, + routing_algorithm_id: intent.routing_algorithm_id, + capture_method: Some(intent.capture_method), + authentication_type: Some(intent.authentication_type), + billing_address: Box::new(intent.billing_address), + shipping_address: Box::new(intent.shipping_address), + customer_present: Some(intent.customer_present), + description: Box::new(intent.description), + return_url: Box::new(intent.return_url), + setup_future_usage: Some(intent.setup_future_usage), + apply_mit_exemption: Some(intent.apply_mit_exemption), + statement_descriptor: Box::new(intent.statement_descriptor), + order_details: Box::new(intent.order_details), + allowed_payment_method_types: Box::new(intent.allowed_payment_method_types), + metadata: Box::new(intent.metadata), + connector_metadata: Box::new(intent.connector_metadata), + feature_metadata: Box::new(intent.feature_metadata), + enable_payment_link: Some(intent.enable_payment_link), + request_incremental_authorization: Some(intent.request_incremental_authorization), + session_expiry: Some(intent.session_expiry), + frm_metadata: Box::new(intent.frm_metadata), + request_external_three_ds_authentication: Some( + intent.request_external_three_ds_authentication, + ), + updated_by: Box::new(intent.updated_by), + }; + let new_payment_intent = db .update_payment_intent( key_manager_state, payment_data.payment_intent, - payment_data.payment_intent_update.clone(), + payment_intent_update, key_store, storage_scheme, ) @@ -270,10 +321,9 @@ impl UpdateTracker, PaymentsUpdateIn .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Could not update Intent")?; - let payment_data = payments::PaymentUpdateData { + let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent: new_payment_intent, - payment_intent_update: payment_data.payment_intent_update, }; Ok((Box::new(self), payment_data)) @@ -281,7 +331,7 @@ impl UpdateTracker, PaymentsUpdateIn } impl - ValidateRequest> + ValidateRequest> for PaymentUpdateIntent { #[instrument(skip_all)] @@ -305,19 +355,19 @@ impl } #[async_trait] -impl Domain> +impl Domain> for PaymentUpdateIntent { #[instrument(skip_all)] async fn get_customer_details<'a>( &'a self, _state: &SessionState, - _payment_data: &mut payments::PaymentUpdateData, + _payment_data: &mut payments::PaymentIntentData, _merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult< ( - BoxedOperation<'a, F, PaymentsUpdateIntentRequest, payments::PaymentUpdateData>, + BoxedOperation<'a, F, PaymentsUpdateIntentRequest, payments::PaymentIntentData>, Option, ), errors::StorageError, @@ -329,7 +379,7 @@ impl Domain( &'a self, _state: &'a SessionState, - _payment_data: &mut payments::PaymentUpdateData, + _payment_data: &mut payments::PaymentIntentData, _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, @@ -348,7 +398,7 @@ impl Domain, + _payment_data: &mut payments::PaymentIntentData, _mechant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { Ok(api::ConnectorCallType::Skip) @@ -360,7 +410,7 @@ impl Domain, + _payment_data: &mut payments::PaymentIntentData, ) -> CustomResult { Ok(false) } diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 95d390ca5498..26e8b1640808 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -219,7 +219,7 @@ pub async fn payments_update_intent( json_payload: web::Json, path: web::Path, ) -> impl Responder { - use hyperswitch_domain_models::payments::PaymentUpdateData; + use hyperswitch_domain_models::payments::PaymentIntentData; let flow = Flow::PaymentsUpdateIntent; let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { @@ -241,13 +241,13 @@ pub async fn payments_update_intent( state, &req, internal_payload, - |state, auth: auth::AuthenticationDataV2, req, req_state| { + |state, auth: auth::AuthenticationData, req, req_state| { payments::payments_intent_core::< api_types::PaymentUpdateIntent, payment_types::PaymentsIntentResponse, _, _, - PaymentUpdateData, + PaymentIntentData, >( state, req_state, From 6e916f354795bcb00c94e46783d3cae3c80e8a00 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Thu, 14 Nov 2024 15:24:15 +0530 Subject: [PATCH 06/26] Fixed clippy --- .../src/payments/payment_intent.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index b57265a5e493..50a73af65cb0 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -20,7 +20,7 @@ use common_utils::{ MinorUnit, }, }; -#[cfg(feature = "v2")] + use diesel_models::{ PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; @@ -38,9 +38,12 @@ use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, - ApiModelToDieselModelConvertor, RemoteStorageObject, + RemoteStorageObject, }; +#[cfg(feature = "v2")] +use crate::ApiModelToDieselModelConvertor; + #[async_trait::async_trait] pub trait PaymentIntentInterface { async fn update_payment_intent( From e94ce870cc0f6ada55f41400af2286065a3cdbd0 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Fri, 15 Nov 2024 15:44:38 +0530 Subject: [PATCH 07/26] Fixes --- .../src/payments/payment_intent.rs | 6 ++-- .../operations/payment_update_intent.rs | 32 +++++-------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 50a73af65cb0..ab08653287dc 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -20,7 +20,6 @@ use common_utils::{ MinorUnit, }, }; - use diesel_models::{ PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; @@ -34,6 +33,8 @@ use time::PrimitiveDateTime; #[cfg(all(feature = "v1", feature = "olap"))] use super::payment_attempt::PaymentAttempt; use super::PaymentIntent; +#[cfg(feature = "v2")] +use crate::ApiModelToDieselModelConvertor; use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, @@ -41,9 +42,6 @@ use crate::{ RemoteStorageObject, }; -#[cfg(feature = "v2")] -use crate::ApiModelToDieselModelConvertor; - #[async_trait::async_trait] pub trait PaymentIntentInterface { async fn update_payment_intent( diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index d75daf64649f..db0fcfba5d9d 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -102,14 +102,7 @@ impl GetTracker, PaymentsUpda profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult< - operations::GetTrackerResponse< - 'a, - F, - PaymentsUpdateIntentRequest, - payments::PaymentIntentData, - >, - > { + ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); let storage_scheme = merchant_account.storage_scheme; @@ -236,10 +229,7 @@ impl GetTracker, PaymentsUpda payment_intent, }; - let get_trackers_response = operations::GetTrackerResponse { - operation: Box::new(self), - payment_data, - }; + let get_trackers_response = operations::GetTrackerResponse { payment_data }; Ok(get_trackers_response) } @@ -339,18 +329,12 @@ impl &'b self, _request: &PaymentsUpdateIntentRequest, merchant_account: &'a domain::MerchantAccount, - ) -> RouterResult<( - PaymentsUpdateIntentOperation<'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, - }, - )) + ) -> RouterResult { + Ok(operations::ValidateResult { + merchant_id: merchant_account.get_id().to_owned(), + storage_scheme: merchant_account.storage_scheme, + requeue: false, + }) } } From 724b2f251b58203d0e708a2f3ee252541bf3da72 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Thu, 28 Nov 2024 14:32:13 +0530 Subject: [PATCH 08/26] Box only a single struct inside UpdateIntent --- crates/api_models/src/payments.rs | 4 +- .../src/payments/payment_intent.rs | 280 +++++++++--------- .../operations/payment_update_intent.rs | 74 ++--- 3 files changed, 180 insertions(+), 178 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ecbda1c07c93..616d7dd0ec97 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -314,11 +314,9 @@ pub struct PaymentsUpdateIntentRequest { pub authentication_type: Option, /// The billing details of the payment. This address will be used for invoicing. - #[schema(value_type = Option
)] pub billing: Option
, /// The shipping address for the payment - #[schema(value_type = Option
)] pub shipping: Option
, /// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. @@ -357,7 +355,7 @@ pub struct PaymentsUpdateIntentRequest { #[schema(value_type = Option>)] pub allowed_payment_method_types: Option>, - /// Metadata is useful for storing additional, unstructured information on an object. + /// Metadata is useful for storing additional, unstructured information on an object. This metadata will override the metadata that was passed in payments #[schema(value_type = Option, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)] pub metadata: Option, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index fedfddbff92b..2cc507163656 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,6 +1,4 @@ #[cfg(feature = "v2")] -use api_models::payments::Address; -#[cfg(feature = "v2")] use common_enums::External3dsAuthenticationRequest; use common_enums::{self as storage_enums}; #[cfg(feature = "v2")] @@ -138,26 +136,61 @@ pub struct CustomerData { #[cfg(feature = "v2")] #[derive(Debug, Clone, Serialize)] pub struct PaymentIntentUpdateFields { + // pub amount: Option, + // pub currency: Option, + // pub setup_future_usage: Option, + // pub status: storage_enums::IntentStatus, + // pub customer_id: Option, + // pub shipping_address: Option>>, + // pub billing_address: Option>>, + // pub return_url: Option, + // pub description: Option, + // pub statement_descriptor: Option, + // pub order_details: Option>, + // pub metadata: Option, + // pub payment_confirm_source: Option, + // pub updated_by: String, + // pub session_expiry: Option, + // pub request_external_three_ds_authentication: Option, + // pub frm_metadata: Option, + // pub customer_details: Option>>, + // pub merchant_order_reference_id: Option, + // pub is_payment_processor_token_flow: Option, pub amount: Option, pub currency: Option, + pub shipping_cost: Option, + // TODO: Check how to handle this + // tax_details: Option, + pub skip_external_tax_calculation: Option, + pub skip_surcharge_calculation: Option, + pub surcharge_amount: Option, + pub tax_on_surcharge: Option, + pub routing_algorithm_id: Option, + pub capture_method: Option, + pub authentication_type: Option, + pub billing_address: Option>, + pub shipping_address: Option>, + pub customer_present: Option, + pub description: Option, + pub return_url: Option, pub setup_future_usage: Option, - pub status: storage_enums::IntentStatus, - pub customer_id: Option, - pub shipping_address: Option>>, - pub billing_address: Option>>, - pub return_url: Option, - pub description: Option, - pub statement_descriptor: Option, - pub order_details: Option>, + pub apply_mit_exemption: Option, + pub statement_descriptor: Option, + pub order_details: Option>>, + pub allowed_payment_method_types: Option>, pub metadata: Option, - pub payment_confirm_source: Option, - pub updated_by: String, + pub connector_metadata: Option, + pub feature_metadata: Option, + pub enable_payment_link: Option, + // TODO: check how to use this + // payment_link_config: Box>, + pub request_incremental_authorization: Option, pub session_expiry: Option, - pub request_external_three_ds_authentication: Option, pub frm_metadata: Option, - pub customer_details: Option>>, - pub merchant_order_reference_id: Option, - pub is_payment_processor_token_flow: Option, + pub request_external_three_ds_authentication: Option, + + // updated_by is set internally, field not present in request + pub updated_by: String, } #[cfg(feature = "v1")] @@ -294,43 +327,7 @@ pub enum PaymentIntentUpdate { updated_by: String, }, /// UpdateIntent - UpdateIntent { - amount: Option, - currency: Option, - shipping_cost: Option, - // TODO: Check how to handle this - // tax_details: Option, - skip_external_tax_calculation: Option, - skip_surcharge_calculation: Option, - surcharge_amount: Option, - tax_on_surcharge: Option, - routing_algorithm_id: Option, - capture_method: Option, - authentication_type: Option, - billing_address: Box>>>, - shipping_address: Box>>>, - customer_present: Option, - description: Box>, - return_url: Box>, - setup_future_usage: Option, - apply_mit_exemption: Option, - statement_descriptor: Box>, - order_details: Box>>>, - allowed_payment_method_types: Box>>, - metadata: Box>, - connector_metadata: Box>, - feature_metadata: Box>, - enable_payment_link: Option, - // TODO: check how to use this - // payment_link_config: Box>, - request_incremental_authorization: Option, - session_expiry: Option, - frm_metadata: Box>, - request_external_three_ds_authentication: Option, - - // updated_by is set internally, field not present in request - updated_by: Box, - }, + UpdateIntent(Box), } #[cfg(feature = "v1")] @@ -489,94 +486,99 @@ impl From for diesel_models::PaymentIntentUpdateInternal { request_external_three_ds_authentication: None, updated_by, }, - PaymentIntentUpdate::UpdateIntent { - amount, - currency, - shipping_cost, - skip_external_tax_calculation, - skip_surcharge_calculation, - surcharge_amount, - tax_on_surcharge, - routing_algorithm_id, - capture_method, - authentication_type, - billing_address, - shipping_address, - customer_present, - description, - return_url, - setup_future_usage, - apply_mit_exemption, - statement_descriptor, - order_details, - allowed_payment_method_types, - metadata, - connector_metadata, - feature_metadata, - enable_payment_link, - request_incremental_authorization, - session_expiry, - frm_metadata, - request_external_three_ds_authentication, - updated_by, - } => Self { - status: None, - active_attempt_id: None, - modified_at: common_utils::date_time::now(), + PaymentIntentUpdate::UpdateIntent(boxed_intent) => { + let PaymentIntentUpdateFields { + amount, + currency, + shipping_cost, + skip_external_tax_calculation, + skip_surcharge_calculation, + surcharge_amount, + tax_on_surcharge, + routing_algorithm_id, + capture_method, + authentication_type, + billing_address, + shipping_address, + customer_present, + description, + return_url, + setup_future_usage, + apply_mit_exemption, + statement_descriptor, + order_details, + allowed_payment_method_types, + metadata, + connector_metadata, + feature_metadata, + enable_payment_link, + request_incremental_authorization, + session_expiry, + frm_metadata, + request_external_three_ds_authentication, + updated_by, + } = *boxed_intent; + Self { + status: None, + active_attempt_id: None, + modified_at: common_utils::date_time::now(), - amount, - currency, - shipping_cost, - skip_external_tax_calculation: skip_external_tax_calculation.map(|val| match val { - super::TaxCalculationOverride::Skip => true, - super::TaxCalculationOverride::Calculate => false, - }), - surcharge_applicable: skip_surcharge_calculation.map(|val| match val { - super::SurchargeCalculationOverride::Skip => true, - super::SurchargeCalculationOverride::Calculate => false, - }), - surcharge_amount, - tax_on_surcharge, - routing_algorithm_id, - capture_method, - authentication_type, - billing_address: billing_address.map(Encryption::from), - shipping_address: shipping_address.map(Encryption::from), - customer_present: customer_present.map(|val| val.as_bool()), - description: *description, - return_url: *return_url, - setup_future_usage, - apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), - statement_descriptor: *statement_descriptor, - order_details: *order_details, - // .map(|order_details| { - // order_details - // .into_iter() - // .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) - // .collect::, _>>() - // }) - // .and_then(|r| r.ok()), - allowed_payment_method_types: allowed_payment_method_types - .map(|allowed_payment_method_types| { - allowed_payment_method_types.encode_to_value() - }) - .and_then(|r| r.ok().map(Secret::new)), - // .transpose() - // .ok().flatten() - // .map(Secret::new), - metadata: *metadata, - connector_metadata: *connector_metadata, - feature_metadata: *feature_metadata, - // .map(|val| diesel_models::types::FeatureMetadata::convert_from(val)), - enable_payment_link: enable_payment_link.map(|val| val.as_bool()), - request_incremental_authorization, - session_expiry, - frm_metadata: *frm_metadata, - request_external_three_ds_authentication: request_external_three_ds_authentication - .map(|val| val.as_bool()), + amount, + currency, + shipping_cost, + skip_external_tax_calculation: skip_external_tax_calculation.map( + |val| match val { + super::TaxCalculationOverride::Skip => true, + super::TaxCalculationOverride::Calculate => false, + }, + ), + surcharge_applicable: skip_surcharge_calculation.map(|val| match val { + super::SurchargeCalculationOverride::Skip => true, + super::SurchargeCalculationOverride::Calculate => false, + }), + surcharge_amount, + tax_on_surcharge, + routing_algorithm_id, + capture_method, + authentication_type, + billing_address: billing_address.map(Encryption::from), + shipping_address: shipping_address.map(Encryption::from), + customer_present: customer_present.map(|val| val.as_bool()), + description: description, + return_url: return_url, + setup_future_usage, + apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), + statement_descriptor: statement_descriptor, + order_details: order_details, + // .map(|order_details| { + // order_details + // .into_iter() + // .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) + // .collect::, _>>() + // }) + // .and_then(|r| r.ok()), + allowed_payment_method_types: allowed_payment_method_types + .map(|allowed_payment_method_types| { + allowed_payment_method_types.encode_to_value() + }) + .and_then(|r| r.ok().map(Secret::new)), + // .transpose() + // .ok().flatten() + // .map(Secret::new), + metadata: metadata, + connector_metadata: connector_metadata, + feature_metadata: feature_metadata, + // .map(|val| diesel_models::types::FeatureMetadata::convert_from(val)), + enable_payment_link: enable_payment_link.map(|val| val.as_bool()), + request_incremental_authorization, + session_expiry, + frm_metadata: frm_metadata, + request_external_three_ds_authentication: + request_external_three_ds_authentication.map(|val| val.as_bool()), - updated_by: *updated_by, - }, + updated_by: updated_by, + } + } } } } diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index db0fcfba5d9d..f2e28f27d062 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -10,7 +10,8 @@ use common_utils::{ use diesel_models::{types::FeatureMetadata, PaymentIntentNew}; use error_stack::ResultExt; use hyperswitch_domain_models::{ - payments::payment_intent::PaymentIntentUpdate, ApiModelToDieselModelConvertor, + payments::payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields}, + ApiModelToDieselModelConvertor, }; use masking::Secret; use router_env::{instrument, tracing}; @@ -263,41 +264,42 @@ impl UpdateTracker, PaymentsUpdateIn let intent = payment_data.payment_intent.clone(); - let payment_intent_update = PaymentIntentUpdate::UpdateIntent { - amount: Some(intent.amount_details.order_amount), - currency: Some(intent.amount_details.currency), - shipping_cost: intent.amount_details.shipping_cost, - skip_external_tax_calculation: Some( - intent.amount_details.skip_external_tax_calculation, - ), - skip_surcharge_calculation: Some(intent.amount_details.skip_surcharge_calculation), - surcharge_amount: intent.amount_details.surcharge_amount, - tax_on_surcharge: intent.amount_details.tax_on_surcharge, - routing_algorithm_id: intent.routing_algorithm_id, - capture_method: Some(intent.capture_method), - authentication_type: Some(intent.authentication_type), - billing_address: Box::new(intent.billing_address), - shipping_address: Box::new(intent.shipping_address), - customer_present: Some(intent.customer_present), - description: Box::new(intent.description), - return_url: Box::new(intent.return_url), - setup_future_usage: Some(intent.setup_future_usage), - apply_mit_exemption: Some(intent.apply_mit_exemption), - statement_descriptor: Box::new(intent.statement_descriptor), - order_details: Box::new(intent.order_details), - allowed_payment_method_types: Box::new(intent.allowed_payment_method_types), - metadata: Box::new(intent.metadata), - connector_metadata: Box::new(intent.connector_metadata), - feature_metadata: Box::new(intent.feature_metadata), - enable_payment_link: Some(intent.enable_payment_link), - request_incremental_authorization: Some(intent.request_incremental_authorization), - session_expiry: Some(intent.session_expiry), - frm_metadata: Box::new(intent.frm_metadata), - request_external_three_ds_authentication: Some( - intent.request_external_three_ds_authentication, - ), - updated_by: Box::new(intent.updated_by), - }; + let payment_intent_update = + PaymentIntentUpdate::UpdateIntent(Box::new(PaymentIntentUpdateFields { + amount: Some(intent.amount_details.order_amount), + currency: Some(intent.amount_details.currency), + shipping_cost: intent.amount_details.shipping_cost, + skip_external_tax_calculation: Some( + intent.amount_details.skip_external_tax_calculation, + ), + skip_surcharge_calculation: Some(intent.amount_details.skip_surcharge_calculation), + surcharge_amount: intent.amount_details.surcharge_amount, + tax_on_surcharge: intent.amount_details.tax_on_surcharge, + routing_algorithm_id: intent.routing_algorithm_id, + capture_method: Some(intent.capture_method), + authentication_type: Some(intent.authentication_type), + billing_address: intent.billing_address, + shipping_address: intent.shipping_address, + customer_present: Some(intent.customer_present), + description: intent.description, + return_url: intent.return_url, + setup_future_usage: Some(intent.setup_future_usage), + apply_mit_exemption: Some(intent.apply_mit_exemption), + statement_descriptor: intent.statement_descriptor, + order_details: intent.order_details, + allowed_payment_method_types: intent.allowed_payment_method_types, + metadata: intent.metadata, + connector_metadata: intent.connector_metadata, + feature_metadata: intent.feature_metadata, + enable_payment_link: Some(intent.enable_payment_link), + request_incremental_authorization: Some(intent.request_incremental_authorization), + session_expiry: Some(intent.session_expiry), + frm_metadata: intent.frm_metadata, + request_external_three_ds_authentication: Some( + intent.request_external_three_ds_authentication, + ), + updated_by: intent.updated_by, + })); let new_payment_intent = db .update_payment_intent( From 3f0c1b7dce3691e6c199bcb5dbf1481b8d2b405f Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Thu, 28 Nov 2024 14:53:06 +0530 Subject: [PATCH 09/26] Remove storage_enums alias and add as_bool impl --- .../hyperswitch_domain_models/src/payments.rs | 20 ++++ .../src/payments/payment_intent.rs | 99 +++++++++---------- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 7cfaeb7f1c90..178a1bfe0c9f 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -183,6 +183,16 @@ impl From> for TaxCalculationOverride { } } +#[cfg(feature = "v2")] +impl TaxCalculationOverride { + fn as_bool(self) -> bool { + match self { + TaxCalculationOverride::Skip => true, + TaxCalculationOverride::Calculate => false, + } + } +} + #[cfg(feature = "v2")] impl From> for SurchargeCalculationOverride { fn from(value: Option) -> Self { @@ -193,6 +203,16 @@ impl From> for SurchargeCalculationOverride { } } +#[cfg(feature = "v2")] +impl SurchargeCalculationOverride { + fn as_bool(self) -> bool { + match self { + SurchargeCalculationOverride::Skip => true, + SurchargeCalculationOverride::Calculate => false, + } + } +} + #[cfg(feature = "v2")] #[derive(Clone, Debug, PartialEq, serde::Serialize)] pub struct AmountDetails { diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 2cc507163656..3a88fc746d5e 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,10 +1,7 @@ #[cfg(feature = "v2")] use common_enums::External3dsAuthenticationRequest; -use common_enums::{self as storage_enums}; #[cfg(feature = "v2")] use common_utils::ext_traits::{Encode, ValueExt}; -#[cfg(feature = "v2")] -use common_utils::types::StatementDescriptor; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, crypto::Encryptable, @@ -48,7 +45,7 @@ pub trait PaymentIntentInterface { this: PaymentIntent, payment_intent: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result; async fn insert_payment_intent( @@ -56,7 +53,7 @@ pub trait PaymentIntentInterface { state: &KeyManagerState, new: PaymentIntent, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result; #[cfg(feature = "v1")] @@ -66,7 +63,7 @@ pub trait PaymentIntentInterface { payment_id: &id_type::PaymentId, merchant_id: &id_type::MerchantId, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result; #[cfg(feature = "v2")] @@ -75,7 +72,7 @@ pub trait PaymentIntentInterface { state: &KeyManagerState, id: &id_type::GlobalPaymentId, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result; #[cfg(all(feature = "v1", feature = "olap"))] @@ -85,7 +82,7 @@ pub trait PaymentIntentInterface { merchant_id: &id_type::MerchantId, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; #[cfg(all(feature = "v1", feature = "olap"))] @@ -95,7 +92,7 @@ pub trait PaymentIntentInterface { merchant_id: &id_type::MerchantId, time_range: &common_utils::types::TimeRange, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; #[cfg(all(feature = "v1", feature = "olap"))] @@ -113,7 +110,7 @@ pub trait PaymentIntentInterface { merchant_id: &id_type::MerchantId, constraints: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; #[cfg(all(feature = "v1", feature = "olap"))] @@ -121,7 +118,7 @@ pub trait PaymentIntentInterface { &self, merchant_id: &id_type::MerchantId, constraints: &PaymentIntentFetchConstraints, - storage_scheme: storage_enums::MerchantStorageScheme, + storage_scheme: common_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; } @@ -137,9 +134,9 @@ pub struct CustomerData { #[derive(Debug, Clone, Serialize)] pub struct PaymentIntentUpdateFields { // pub amount: Option, - // pub currency: Option, - // pub setup_future_usage: Option, - // pub status: storage_enums::IntentStatus, + // pub currency: Option, + // pub setup_future_usage: Option, + // pub status: common_enums::IntentStatus, // pub customer_id: Option, // pub shipping_address: Option>>, // pub billing_address: Option>>, @@ -148,7 +145,7 @@ pub struct PaymentIntentUpdateFields { // pub statement_descriptor: Option, // pub order_details: Option>, // pub metadata: Option, - // pub payment_confirm_source: Option, + // pub payment_confirm_source: Option, // pub updated_by: String, // pub session_expiry: Option, // pub request_external_three_ds_authentication: Option, @@ -157,7 +154,7 @@ pub struct PaymentIntentUpdateFields { // pub merchant_order_reference_id: Option, // pub is_payment_processor_token_flow: Option, pub amount: Option, - pub currency: Option, + pub currency: Option, pub shipping_cost: Option, // TODO: Check how to handle this // tax_details: Option, @@ -173,9 +170,9 @@ pub struct PaymentIntentUpdateFields { pub customer_present: Option, pub description: Option, pub return_url: Option, - pub setup_future_usage: Option, + pub setup_future_usage: Option, pub apply_mit_exemption: Option, - pub statement_descriptor: Option, + pub statement_descriptor: Option, pub order_details: Option>>, pub allowed_payment_method_types: Option>, pub metadata: Option, @@ -197,14 +194,14 @@ pub struct PaymentIntentUpdateFields { #[derive(Debug, Clone, Serialize)] pub struct PaymentIntentUpdateFields { pub amount: MinorUnit, - pub currency: storage_enums::Currency, - pub setup_future_usage: Option, - pub status: storage_enums::IntentStatus, + pub currency: common_enums::Currency, + pub setup_future_usage: Option, + pub status: common_enums::IntentStatus, pub customer_id: Option, pub shipping_address_id: Option, pub billing_address_id: Option, pub return_url: Option, - pub business_country: Option, + pub business_country: Option, pub business_label: Option, pub description: Option, pub statement_descriptor_name: Option, @@ -212,7 +209,7 @@ pub struct PaymentIntentUpdateFields { pub order_details: Option>, pub metadata: Option, pub frm_metadata: Option, - pub payment_confirm_source: Option, + pub payment_confirm_source: Option, pub updated_by: String, pub fingerprint_id: Option, pub session_expiry: Option, @@ -229,7 +226,7 @@ pub struct PaymentIntentUpdateFields { #[derive(Debug, Clone, Serialize)] pub enum PaymentIntentUpdate { ResponseUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, amount_captured: Option, return_url: Option, updated_by: String, @@ -243,7 +240,7 @@ pub enum PaymentIntentUpdate { Update(Box), PaymentCreateUpdate { return_url: Option, - status: Option, + status: Option, customer_id: Option, shipping_address_id: Option, billing_address_id: Option, @@ -251,13 +248,13 @@ pub enum PaymentIntentUpdate { updated_by: String, }, MerchantStatusUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, shipping_address_id: Option, billing_address_id: Option, updated_by: String, }, PGStatusUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, incremental_authorization_allowed: Option, updated_by: String, }, @@ -267,18 +264,18 @@ pub enum PaymentIntentUpdate { updated_by: String, }, StatusAndAttemptUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, active_attempt_id: String, attempt_count: i16, updated_by: String, }, ApproveUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, merchant_decision: Option, updated_by: String, }, RejectUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, merchant_decision: Option, updated_by: String, }, @@ -296,7 +293,7 @@ pub enum PaymentIntentUpdate { shipping_address_id: Option, }, ManualUpdate { - status: Option, + status: Option, updated_by: String, }, SessionResponseUpdate { @@ -312,18 +309,18 @@ pub enum PaymentIntentUpdate { pub enum PaymentIntentUpdate { /// PreUpdate tracker of ConfirmIntent ConfirmIntent { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, active_attempt_id: id_type::GlobalAttemptId, updated_by: String, }, /// PostUpdate tracker of ConfirmIntent ConfirmIntentPostUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, updated_by: String, }, /// SyncUpdate of ConfirmIntent in PostUpdateTrackers SyncUpdate { - status: storage_enums::IntentStatus, + status: common_enums::IntentStatus, updated_by: String, }, /// UpdateIntent @@ -334,19 +331,19 @@ pub enum PaymentIntentUpdate { #[derive(Clone, Debug, Default)] pub struct PaymentIntentUpdateInternal { pub amount: Option, - pub currency: Option, - pub status: Option, + pub currency: Option, + pub status: Option, pub amount_captured: Option, pub customer_id: Option, pub return_url: Option, - pub setup_future_usage: Option, + pub setup_future_usage: Option, pub off_session: Option, pub metadata: Option, pub billing_address_id: Option, pub shipping_address_id: Option, pub modified_at: Option, pub active_attempt_id: Option, - pub business_country: Option, + pub business_country: Option, pub business_label: Option, pub description: Option, pub statement_descriptor_name: Option, @@ -356,7 +353,7 @@ pub struct PaymentIntentUpdateInternal { // Denotes the action(approve or reject) taken by merchant in case of manual review. // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment pub merchant_decision: Option, - pub payment_confirm_source: Option, + pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, @@ -526,16 +523,8 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount, currency, shipping_cost, - skip_external_tax_calculation: skip_external_tax_calculation.map( - |val| match val { - super::TaxCalculationOverride::Skip => true, - super::TaxCalculationOverride::Calculate => false, - }, - ), - surcharge_applicable: skip_surcharge_calculation.map(|val| match val { - super::SurchargeCalculationOverride::Skip => true, - super::SurchargeCalculationOverride::Calculate => false, - }), + skip_external_tax_calculation: skip_external_tax_calculation.map(|val| val.as_bool()), + surcharge_applicable: skip_surcharge_calculation.map(|val| val.as_bool()), surcharge_amount, tax_on_surcharge, routing_algorithm_id, @@ -1078,11 +1067,11 @@ pub struct PaymentIntentListParams { pub ending_at: Option, pub amount_filter: Option, pub connector: Option>, - pub currency: Option>, - pub status: Option>, - pub payment_method: Option>, - pub payment_method_type: Option>, - pub authentication_type: Option>, + pub currency: Option>, + pub status: Option>, + pub payment_method: Option>, + pub payment_method_type: Option>, + pub authentication_type: Option>, pub merchant_connector_id: Option>, pub profile_id: Option>, pub customer_id: Option, @@ -1090,7 +1079,7 @@ pub struct PaymentIntentListParams { pub ending_before_id: Option, pub limit: Option, pub order: api_models::payments::Order, - pub card_network: Option>, + pub card_network: Option>, pub merchant_order_reference_id: Option, } From 49bf10d0caa42f9bb47edf46917171be8f3f32f8 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Fri, 29 Nov 2024 13:19:47 +0530 Subject: [PATCH 10/26] Add AmountDetailsUpdate struct --- crates/api_models/src/payments.rs | 58 ++++++++++++++++++- .../hyperswitch_domain_models/src/payments.rs | 19 +++--- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 616d7dd0ec97..96f535c29b87 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -301,7 +301,7 @@ pub struct PaymentsGetIntentRequest { #[serde(deny_unknown_fields)] #[cfg(feature = "v2")] pub struct PaymentsUpdateIntentRequest { - pub amount_details: Option, + pub amount_details: Option, /// The routing algorithm id to be used for the payment #[schema(value_type = Option)] @@ -546,6 +546,35 @@ pub struct AmountDetails { tax_on_surcharge: Option, } + +#[cfg(feature = "v2")] +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] +pub struct AmountDetailsUpdate { + /// The payment amount. Amount for the payment in the lowest denomination of the currency, (i.e) in cents for USD denomination, in yen for JPY denomination etc. E.g., Pass 100 to charge $1.00 and 1 for 1¥ since ¥ is a zero-decimal currency. Read more about [the Decimal and Non-Decimal Currencies](https://github.com/juspay/hyperswitch/wiki/Decimal-and-Non%E2%80%90Decimal-Currencies) + #[schema(value_type = Option, example = 6540)] + #[serde(default, deserialize_with = "amount::deserialize_option")] + order_amount: Option, + /// The currency of the order + #[schema(example = "USD", value_type = Option)] + currency: Option, + /// The shipping cost of the order. This has to be collected from the merchant + shipping_cost: Option, + /// Tax amount related to the order. This will be calculated by the external tax provider + order_tax_amount: Option, + /// The action to whether calculate tax by calling external tax provider or not + #[serde(default)] + #[schema(value_type = Option)] + skip_external_tax_calculation: Option, + /// The action to whether calculate surcharge or not + #[serde(default)] + #[schema(value_type = Option)] + skip_surcharge_calculation: Option, + /// The surcharge amount to be added to the order, collected from the merchant + surcharge_amount: Option, + /// tax on surcharge amount + tax_on_surcharge: Option, +} + #[cfg(feature = "v2")] pub struct AmountDetailsSetter { pub order_amount: Amount, @@ -658,6 +687,33 @@ impl AmountDetails { } } +#[cfg(feature = "v2")] +impl AmountDetailsUpdate { + pub fn order_amount(&self) -> Option { + self.order_amount + } + pub fn currency(&self) -> Option { + self.currency + } + pub fn shipping_cost(&self) -> Option { + self.shipping_cost + } + pub fn order_tax_amount(&self) -> Option { + self.order_tax_amount + } + pub fn skip_external_tax_calculation(&self) -> Option { + self.skip_external_tax_calculation.clone() + } + pub fn skip_surcharge_calculation(&self) -> Option { + self.skip_surcharge_calculation.clone() + } + pub fn surcharge_amount(&self) -> Option { + self.surcharge_amount + } + pub fn tax_on_surcharge(&self) -> Option { + self.tax_on_surcharge + } +} #[cfg(feature = "v1")] #[derive( Default, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 178a1bfe0c9f..1fa0b6e5f372 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -183,12 +183,13 @@ impl From> for TaxCalculationOverride { } } + #[cfg(feature = "v2")] impl TaxCalculationOverride { fn as_bool(self) -> bool { match self { - TaxCalculationOverride::Skip => true, - TaxCalculationOverride::Calculate => false, + TaxCalculationOverride::Skip => false, + TaxCalculationOverride::Calculate => true, } } } @@ -207,8 +208,8 @@ impl From> for SurchargeCalculationOverride { impl SurchargeCalculationOverride { fn as_bool(self) -> bool { match self { - SurchargeCalculationOverride::Skip => true, - SurchargeCalculationOverride::Calculate => false, + SurchargeCalculationOverride::Skip => false, + SurchargeCalculationOverride::Calculate => true, } } } @@ -298,14 +299,14 @@ impl AmountDetails { } } - pub fn update_from_request(self, req: &api_models::payments::AmountDetails) -> Self { + pub fn update_from_request(self, req: &api_models::payments::AmountDetailsUpdate) -> Self { Self { - order_amount: req.order_amount().into(), - currency: req.currency(), + order_amount: req.order_amount().unwrap_or(self.order_amount.into()).into(), + currency: req.currency().unwrap_or(self.currency), shipping_cost: req.shipping_cost().or(self.shipping_cost), tax_details: self.tax_details, - skip_external_tax_calculation: self.skip_external_tax_calculation, - skip_surcharge_calculation: self.skip_surcharge_calculation, + skip_external_tax_calculation: req.skip_external_tax_calculation().map(Into::into).unwrap_or(self.skip_external_tax_calculation), + skip_surcharge_calculation: req.skip_surcharge_calculation().map(Into::into).unwrap_or(self.skip_surcharge_calculation), surcharge_amount: req.surcharge_amount().or(self.surcharge_amount), tax_on_surcharge: req.tax_on_surcharge().or(self.tax_on_surcharge), amount_captured: self.amount_captured, From 8d5c27feebe8fa49caa967d3145917c2006c1733 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Fri, 29 Nov 2024 13:32:23 +0530 Subject: [PATCH 11/26] Updated Comment --- crates/router/src/core/payments.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index a22ba57c9e39..e0f8eaa06b26 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1016,7 +1016,8 @@ where .to_validate_request()? .validate_request(&req, &merchant_account)?; - // If payment_id is not present in request, generate new payment_id + // If payment_id has not been generated (Create-Intent), generate new payment_id, otherwise use existing + // payment_id (Get-Intent, Update-Intent etc.) let payment_id = match payment_id { Some(id) => id, None => id_type::GlobalPaymentId::generate(state.conf.cell_information.id.clone()), From 6e0ff5d865fa8abb0b184bbf3c39bbeaa3558714 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Fri, 29 Nov 2024 13:36:04 +0530 Subject: [PATCH 12/26] Updated OpenAPI spec, cleanup --- api-reference-v2/openapi_spec.json | 85 ++++++++++++++++++- crates/api_models/src/payments.rs | 1 - .../hyperswitch_domain_models/src/payments.rs | 16 +++- .../src/payments/payment_intent.rs | 3 +- crates/openapi/src/openapi_v2.rs | 1 + crates/openapi/src/routes/payments.rs | 8 +- .../operations/payment_update_intent.rs | 5 +- 7 files changed, 107 insertions(+), 12 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 9e7546fd2c40..3af03cd7421f 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1928,6 +1928,18 @@ "schema": { "type": "string" } + }, + { + "name": "X-Profile-Id", + "in": "header", + "description": "Profile ID associated to the payment intent", + "required": true, + "schema": { + "type": "string" + }, + "example": { + "X-Profile-Id": "pro_abcdefghijklmnop" + } } ], "requestBody": { @@ -2761,6 +2773,75 @@ } } }, + "AmountDetailsUpdate": { + "type": "object", + "properties": { + "order_amount": { + "type": "integer", + "format": "int64", + "description": "The payment amount. Amount for the payment in the lowest denomination of the currency, (i.e) in cents for USD denomination, in yen for JPY denomination etc. E.g., Pass 100 to charge $1.00 and 1 for 1¥ since ¥ is a zero-decimal currency. Read more about [the Decimal and Non-Decimal Currencies](https://github.com/juspay/hyperswitch/wiki/Decimal-and-Non%E2%80%90Decimal-Currencies)", + "example": 6540, + "nullable": true, + "minimum": 0 + }, + "currency": { + "allOf": [ + { + "$ref": "#/components/schemas/Currency" + } + ], + "nullable": true + }, + "shipping_cost": { + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], + "nullable": true + }, + "order_tax_amount": { + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], + "nullable": true + }, + "skip_external_tax_calculation": { + "allOf": [ + { + "$ref": "#/components/schemas/TaxCalculationOverride" + } + ], + "nullable": true + }, + "skip_surcharge_calculation": { + "allOf": [ + { + "$ref": "#/components/schemas/SurchargeCalculationOverride" + } + ], + "nullable": true + }, + "surcharge_amount": { + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], + "nullable": true + }, + "tax_on_surcharge": { + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], + "nullable": true + } + } + }, "AmountFilter": { "type": "object", "properties": { @@ -15432,7 +15513,7 @@ "amount_details": { "allOf": [ { - "$ref": "#/components/schemas/AmountDetails" + "$ref": "#/components/schemas/AmountDetailsUpdate" } ], "nullable": true @@ -15537,7 +15618,7 @@ }, "metadata": { "type": "object", - "description": "Metadata is useful for storing additional, unstructured information on an object.", + "description": "Metadata is useful for storing additional, unstructured information on an object. This metadata will override the metadata that was passed in payments", "nullable": true }, "connector_metadata": { diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 96f535c29b87..22c36a91a678 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -546,7 +546,6 @@ pub struct AmountDetails { tax_on_surcharge: Option, } - #[cfg(feature = "v2")] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] pub struct AmountDetailsUpdate { diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 1fa0b6e5f372..7ed0aa782771 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -183,7 +183,6 @@ impl From> for TaxCalculationOverride { } } - #[cfg(feature = "v2")] impl TaxCalculationOverride { fn as_bool(self) -> bool { @@ -301,12 +300,21 @@ impl AmountDetails { pub fn update_from_request(self, req: &api_models::payments::AmountDetailsUpdate) -> Self { Self { - order_amount: req.order_amount().unwrap_or(self.order_amount.into()).into(), + order_amount: req + .order_amount() + .unwrap_or(self.order_amount.into()) + .into(), currency: req.currency().unwrap_or(self.currency), shipping_cost: req.shipping_cost().or(self.shipping_cost), tax_details: self.tax_details, - skip_external_tax_calculation: req.skip_external_tax_calculation().map(Into::into).unwrap_or(self.skip_external_tax_calculation), - skip_surcharge_calculation: req.skip_surcharge_calculation().map(Into::into).unwrap_or(self.skip_surcharge_calculation), + skip_external_tax_calculation: req + .skip_external_tax_calculation() + .map(Into::into) + .unwrap_or(self.skip_external_tax_calculation), + skip_surcharge_calculation: req + .skip_surcharge_calculation() + .map(Into::into) + .unwrap_or(self.skip_surcharge_calculation), surcharge_amount: req.surcharge_amount().or(self.surcharge_amount), tax_on_surcharge: req.tax_on_surcharge().or(self.tax_on_surcharge), amount_captured: self.amount_captured, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 3a88fc746d5e..33a4e9b9ec0f 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -523,7 +523,8 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount, currency, shipping_cost, - skip_external_tax_calculation: skip_external_tax_calculation.map(|val| val.as_bool()), + skip_external_tax_calculation: skip_external_tax_calculation + .map(|val| val.as_bool()), surcharge_applicable: skip_surcharge_calculation.map(|val| val.as_bool()), surcharge_amount, tax_on_surcharge, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 3ad9f1a892ec..acc0df92b5db 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -343,6 +343,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentsIntentResponse, api_models::payments::PazeWalletData, api_models::payments::AmountDetails, + api_models::payments::AmountDetailsUpdate, api_models::payments::SessionToken, api_models::payments::ApplePaySessionResponse, api_models::payments::ThirdPartySdkSessionResponse, diff --git a/crates/openapi/src/routes/payments.rs b/crates/openapi/src/routes/payments.rs index 8b8ec1aec326..8f0fea6169d0 100644 --- a/crates/openapi/src/routes/payments.rs +++ b/crates/openapi/src/routes/payments.rs @@ -661,7 +661,13 @@ pub fn payments_get_intent() {} #[utoipa::path( put, path = "/v2/payments/{id}/update-intent", - params (("id" = String, Path, description = "The unique identifier for the Payment Intent")), + params (("id" = String, Path, description = "The unique identifier for the Payment Intent"), + ( + "X-Profile-Id" = String, Header, + description = "Profile ID associated to the payment intent", + example = json!({"X-Profile-Id": "pro_abcdefghijklmnop"}) + ), + ), request_body( content = PaymentsUpdateIntentRequest, examples( diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index f2e28f27d062..842539383193 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -5,9 +5,8 @@ use async_trait::async_trait; use common_utils::{ errors::CustomResult, ext_traits::{AsyncExt, ValueExt}, - types::MinorUnit, }; -use diesel_models::{types::FeatureMetadata, PaymentIntentNew}; +use diesel_models::types::FeatureMetadata; use error_stack::ResultExt; use hyperswitch_domain_models::{ payments::payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields}, @@ -100,7 +99,7 @@ impl GetTracker, PaymentsUpda payment_id: &common_utils::id_type::GlobalPaymentId, request: &PaymentsUpdateIntentRequest, merchant_account: &domain::MerchantAccount, - profile: &domain::Profile, + _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, ) -> RouterResult>> { From 1d4ca14d0bd0ef3b822011f79bde5e83e1fc3189 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Fri, 29 Nov 2024 14:10:15 +0530 Subject: [PATCH 13/26] Fix clippy --- .../hyperswitch_domain_models/src/payments.rs | 8 ++++---- .../src/payments/payment_intent.rs | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 7ed0aa782771..88739f660d03 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -187,8 +187,8 @@ impl From> for TaxCalculationOverride { impl TaxCalculationOverride { fn as_bool(self) -> bool { match self { - TaxCalculationOverride::Skip => false, - TaxCalculationOverride::Calculate => true, + Self::Skip => false, + Self::Calculate => true, } } } @@ -207,8 +207,8 @@ impl From> for SurchargeCalculationOverride { impl SurchargeCalculationOverride { fn as_bool(self) -> bool { match self { - SurchargeCalculationOverride::Skip => false, - SurchargeCalculationOverride::Calculate => true, + Self::Skip => false, + Self::Calculate => true, } } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 33a4e9b9ec0f..60ba53310eec 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -534,12 +534,12 @@ impl From for diesel_models::PaymentIntentUpdateInternal { billing_address: billing_address.map(Encryption::from), shipping_address: shipping_address.map(Encryption::from), customer_present: customer_present.map(|val| val.as_bool()), - description: description, - return_url: return_url, + description, + return_url, setup_future_usage, apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), - statement_descriptor: statement_descriptor, - order_details: order_details, + statement_descriptor, + order_details, // .map(|order_details| { // order_details // .into_iter() @@ -555,18 +555,18 @@ impl From for diesel_models::PaymentIntentUpdateInternal { // .transpose() // .ok().flatten() // .map(Secret::new), - metadata: metadata, - connector_metadata: connector_metadata, - feature_metadata: feature_metadata, + metadata, + connector_metadata, + feature_metadata, // .map(|val| diesel_models::types::FeatureMetadata::convert_from(val)), enable_payment_link: enable_payment_link.map(|val| val.as_bool()), request_incremental_authorization, session_expiry, - frm_metadata: frm_metadata, + frm_metadata, request_external_three_ds_authentication: request_external_three_ds_authentication.map(|val| val.as_bool()), - updated_by: updated_by, + updated_by, } } } From d6a84f61ece0548c6c00a13d7d2df20c633f76e7 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Mon, 2 Dec 2024 15:12:08 +0530 Subject: [PATCH 14/26] Fix clippy --- .../src/core/payments/operations/payment_update_intent.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 842539383193..09d41a0dd72d 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -227,6 +227,7 @@ impl GetTracker, PaymentsUpda let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, + sessions_token: vec![], }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; @@ -315,6 +316,7 @@ impl UpdateTracker, PaymentsUpdateIn let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent: new_payment_intent, + sessions_token: vec![], }; Ok((Box::new(self), payment_data)) From b87e1ccf21139dc900d345e387ce2ecf9a8050d3 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Tue, 3 Dec 2024 14:54:25 +0530 Subject: [PATCH 15/26] Address review comments --- .../src/payments/payment_intent.rs | 54 ++----- crates/router/src/core/payments.rs | 11 +- .../operations/payment_update_intent.rs | 135 ++++++++++++------ crates/router/src/routes/payments.rs | 9 +- 4 files changed, 104 insertions(+), 105 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 60ba53310eec..a1e8e8c74be3 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,6 +1,4 @@ #[cfg(feature = "v2")] -use common_enums::External3dsAuthenticationRequest; -#[cfg(feature = "v2")] use common_utils::ext_traits::{Encode, ValueExt}; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, @@ -133,26 +131,6 @@ pub struct CustomerData { #[cfg(feature = "v2")] #[derive(Debug, Clone, Serialize)] pub struct PaymentIntentUpdateFields { - // pub amount: Option, - // pub currency: Option, - // pub setup_future_usage: Option, - // pub status: common_enums::IntentStatus, - // pub customer_id: Option, - // pub shipping_address: Option>>, - // pub billing_address: Option>>, - // pub return_url: Option, - // pub description: Option, - // pub statement_descriptor: Option, - // pub order_details: Option>, - // pub metadata: Option, - // pub payment_confirm_source: Option, - // pub updated_by: String, - // pub session_expiry: Option, - // pub request_external_three_ds_authentication: Option, - // pub frm_metadata: Option, - // pub customer_details: Option>>, - // pub merchant_order_reference_id: Option, - // pub is_payment_processor_token_flow: Option, pub amount: Option, pub currency: Option, pub shipping_cost: Option, @@ -184,7 +162,8 @@ pub struct PaymentIntentUpdateFields { pub request_incremental_authorization: Option, pub session_expiry: Option, pub frm_metadata: Option, - pub request_external_three_ds_authentication: Option, + pub request_external_three_ds_authentication: + Option, // updated_by is set internally, field not present in request pub updated_by: String, @@ -540,25 +519,14 @@ impl From for diesel_models::PaymentIntentUpdateInternal { apply_mit_exemption: apply_mit_exemption.map(|val| val.as_bool()), statement_descriptor, order_details, - // .map(|order_details| { - // order_details - // .into_iter() - // .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) - // .collect::, _>>() - // }) - // .and_then(|r| r.ok()), allowed_payment_method_types: allowed_payment_method_types .map(|allowed_payment_method_types| { allowed_payment_method_types.encode_to_value() }) .and_then(|r| r.ok().map(Secret::new)), - // .transpose() - // .ok().flatten() - // .map(Secret::new), metadata, connector_metadata, feature_metadata, - // .map(|val| diesel_models::types::FeatureMetadata::convert_from(val)), enable_payment_link: enable_payment_link.map(|val| val.as_bool()), request_incremental_authorization, session_expiry, @@ -1475,9 +1443,9 @@ impl behaviour::Conversion for PaymentIntent { .unwrap_or_default(), authorization_count: storage_model.authorization_count, session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: External3dsAuthenticationRequest::from( - storage_model.request_external_three_ds_authentication, - ), + request_external_three_ds_authentication: storage_model + .request_external_three_ds_authentication + .into(), frm_metadata: storage_model.frm_metadata, customer_details: data.customer_details, billing_address, @@ -1488,15 +1456,9 @@ impl behaviour::Conversion for PaymentIntent { organization_id: storage_model.organization_id, authentication_type: storage_model.authentication_type.unwrap_or_default(), prerouting_algorithm: storage_model.prerouting_algorithm, - enable_payment_link: common_enums::EnablePaymentLinkRequest::from( - storage_model.enable_payment_link, - ), - apply_mit_exemption: common_enums::MitExemptionRequest::from( - storage_model.apply_mit_exemption, - ), - customer_present: common_enums::PresenceOfCustomerDuringPayment::from( - storage_model.customer_present, - ), + enable_payment_link: storage_model.enable_payment_link.into(), + apply_mit_exemption: storage_model.apply_mit_exemption.into(), + customer_present: storage_model.customer_present.into(), payment_link_config: storage_model.payment_link_config, routing_algorithm_id: storage_model.routing_algorithm_id, }) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 08ddaa513260..0a813db3a05f 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1003,7 +1003,7 @@ pub async fn payments_intent_operation_core( key_store: domain::MerchantKeyStore, operation: Op, req: Req, - payment_id: Option, + payment_id: id_type::GlobalPaymentId, header_payload: HeaderPayload, ) -> RouterResult<(D, Req, Option)> where @@ -1020,13 +1020,6 @@ where .to_validate_request()? .validate_request(&req, &merchant_account)?; - // If payment_id has not been generated (Create-Intent), generate new payment_id, otherwise use existing - // payment_id (Get-Intent, Update-Intent etc.) - let payment_id = match payment_id { - Some(id) => id, - None => id_type::GlobalPaymentId::generate(state.conf.cell_information.id.clone()), - }; - tracing::Span::current().record("global_payment_id", payment_id.get_string_repr()); let operations::GetTrackerResponse { mut payment_data } = operation @@ -1454,7 +1447,7 @@ pub async fn payments_intent_core( key_store: domain::MerchantKeyStore, operation: Op, req: Req, - payment_id: Option, + payment_id: id_type::GlobalPaymentId, header_payload: HeaderPayload, ) -> RouterResponse where diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 09d41a0dd72d..e065ff7f1ff5 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -4,7 +4,8 @@ use api_models::{enums::FrmSuggestion, payments::PaymentsUpdateIntentRequest}; use async_trait::async_trait; use common_utils::{ errors::CustomResult, - ext_traits::{AsyncExt, ValueExt}, + ext_traits::{Encode, ValueExt}, + types::keymanager::ToEncryptable, }; use diesel_models::types::FeatureMetadata; use error_stack::ResultExt; @@ -12,20 +13,23 @@ use hyperswitch_domain_models::{ payments::payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields}, ApiModelToDieselModelConvertor, }; -use masking::Secret; +use masking::PeekInterface; use router_env::{instrument, tracing}; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; use crate::{ core::{ errors::{self, RouterResult}, - payment_methods::cards::create_encrypted_data, - payments::{self, operations}, + payments::{ + self, + operations::{self, ValidateStatusForOperation}, + }, }, db::errors::StorageErrorExt, routes::{app::ReqState, SessionState}, types::{ - api, domain, + api, + domain::{self, types as domain_types}, storage::{self, enums}, }, }; @@ -33,6 +37,35 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub struct PaymentUpdateIntent; +impl ValidateStatusForOperation for PaymentUpdateIntent { + /// Validate if the current operation can be performed on the current status of the payment intent + fn validate_status_for_operation( + &self, + intent_status: common_enums::IntentStatus, + ) -> Result<(), errors::ApiErrorResponse> { + match intent_status { + common_enums::IntentStatus::RequiresPaymentMethod => Ok(()), + common_enums::IntentStatus::Succeeded + | common_enums::IntentStatus::Failed + | common_enums::IntentStatus::Cancelled + | common_enums::IntentStatus::Processing + | common_enums::IntentStatus::RequiresCustomerAction + | common_enums::IntentStatus::RequiresMerchantAction + | common_enums::IntentStatus::RequiresCapture + | common_enums::IntentStatus::PartiallyCaptured + | common_enums::IntentStatus::RequiresConfirmation + | common_enums::IntentStatus::PartiallyCapturedAndCapturable => { + Err(errors::ApiErrorResponse::PaymentUnexpectedState { + current_flow: format!("{self:?}"), + field_name: "status".to_string(), + current_value: intent_status.to_string(), + states: ["requires_payment_method".to_string()].join(", "), + }) + } + } + } +} + impl Operation for &PaymentUpdateIntent { type Data = payments::PaymentIntentData; fn to_validate_request( @@ -111,6 +144,8 @@ impl GetTracker, PaymentsUpda .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + self.validate_status_for_operation(payment_intent.status)?; + let PaymentsUpdateIntentRequest { amount_details, routing_algorithm_id, @@ -137,46 +172,38 @@ impl GetTracker, PaymentsUpda request_external_three_ds_authentication, } = request.clone(); - // TODO: Use Batch Encryption - let billing_address = billing - .clone() - .async_map(|billing_details| { - create_encrypted_data(key_manager_state, key_store, billing_details) - }) - .await - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt billing details")? - .map(|encrypted_value| { - encrypted_value.deserialize_inner_value(|value| value.parse_value("Address")) - }) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to deserialize decrypted value to Address")?; + let batch_encrypted_data = domain_types::crypto_operation( + key_manager_state, + common_utils::type_name!(hyperswitch_domain_models::payments::PaymentIntent), + domain_types::CryptoOperation::BatchEncrypt( + hyperswitch_domain_models::payments::FromRequestEncryptablePaymentIntent::to_encryptable( + hyperswitch_domain_models::payments::FromRequestEncryptablePaymentIntent { + shipping_address: shipping.map(|address| address.encode_to_value()).transpose().change_context(errors::ApiErrorResponse::InternalServerError).attach_printable("Failed to encode shipping address")?.map(masking::Secret::new), + billing_address: billing.map(|address| address.encode_to_value()).transpose().change_context(errors::ApiErrorResponse::InternalServerError).attach_printable("Failed to encode billing address")?.map(masking::Secret::new), + customer_details: None, + }, + ), + ), + common_utils::types::keymanager::Identifier::Merchant(merchant_account.get_id().to_owned()), + key_store.key.peek(), + ) + .await + .and_then(|val| val.try_into_batchoperation()) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting payment intent details".to_string())?; - let shipping_address = shipping - .clone() - .async_map(|shipping_details| { - create_encrypted_data(key_manager_state, key_store, shipping_details) - }) - .await - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt shipping details")? - .map(|encrypted_value| { - encrypted_value.deserialize_inner_value(|value| value.parse_value("Address")) - }) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to deserialize decrypted value to Address")?; + let decrypted_payment_intent = + hyperswitch_domain_models::payments::FromRequestEncryptablePaymentIntent::from_encryptable(batch_encrypted_data) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting payment intent details")?; let order_details = order_details.clone().map(|order_details| { order_details .into_iter() .map(|order_detail| { - Secret::new(diesel_models::types::OrderDetailsWithAmount::convert_from( - order_detail, - )) + masking::Secret::new( + diesel_models::types::OrderDetailsWithAmount::convert_from(order_detail), + ) }) .collect() }); @@ -211,8 +238,26 @@ impl GetTracker, PaymentsUpda request_external_three_ds_authentication: request_external_three_ds_authentication .unwrap_or(payment_intent.request_external_three_ds_authentication), frm_metadata: frm_metadata.or(payment_intent.frm_metadata), - billing_address, - shipping_address, + billing_address: decrypted_payment_intent + .billing_address + .as_ref() + .map(|data| { + data.clone() + .deserialize_inner_value(|value| value.parse_value("Address")) + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to decode billing address")?, + shipping_address: decrypted_payment_intent + .shipping_address + .as_ref() + .map(|data| { + data.clone() + .deserialize_inner_value(|value| value.parse_value("Address")) + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to decode shipping address")?, capture_method: capture_method.unwrap_or(payment_intent.capture_method), authentication_type: authentication_type.unwrap_or(payment_intent.authentication_type), enable_payment_link: enable_payment_link.unwrap_or(payment_intent.enable_payment_link), @@ -245,7 +290,7 @@ impl UpdateTracker, PaymentsUpdateIn &'b self, state: &'b SessionState, _req_state: ReqState, - payment_data: payments::PaymentIntentData, + mut payment_data: payments::PaymentIntentData, _customer: Option, storage_scheme: enums::MerchantStorageScheme, _updated_customer: Option, @@ -313,11 +358,7 @@ impl UpdateTracker, PaymentsUpdateIn .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Could not update Intent")?; - let payment_data = payments::PaymentIntentData { - flow: PhantomData, - payment_intent: new_payment_intent, - sessions_token: vec![], - }; + payment_data.payment_intent = new_payment_intent; Ok((Box::new(self), payment_data)) } diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 4c7ea1ee1779..d06badfeafc3 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -118,6 +118,9 @@ pub async fn payments_create_intent( return api::log_and_return_error_response(err); } }; + let global_payment_id = + common_utils::id_type::GlobalPaymentId::generate(state.conf.cell_information.id.clone()); + Box::pin(api::server_wrap( flow, state, @@ -138,7 +141,7 @@ pub async fn payments_create_intent( auth.key_store, payments::operations::PaymentIntentCreate, req, - None, + global_payment_id.clone(), header_payload.clone(), ) }, @@ -201,7 +204,7 @@ pub async fn payments_get_intent( auth.key_store, payments::operations::PaymentGetIntent, req, - Some(global_payment_id.clone()), + global_payment_id.clone(), header_payload.clone(), ) }, @@ -256,7 +259,7 @@ pub async fn payments_update_intent( auth.key_store, payments::operations::PaymentUpdateIntent, req.payload, - Some(global_payment_id.clone()), + global_payment_id.clone(), header_payload.clone(), ) }, From 2c4a7055cb3ca560af00584f4ae967dd0345e29b Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Tue, 3 Dec 2024 16:22:39 +0530 Subject: [PATCH 16/26] Fix clippy --- crates/router/src/routes/payments.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index d3a5f72774bf..7a8e65e0604f 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -119,7 +119,7 @@ pub async fn payments_create_intent( } }; let global_payment_id = - common_utils::id_type::GlobalPaymentId::generate(state.conf.cell_information.id.clone()); + common_utils::id_type::GlobalPaymentId::generate(&state.conf.cell_information.id.clone()); Box::pin(api::server_wrap( flow, From 9758969f9e502906c5448ba6982114fc508e245a Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 15:10:29 +0530 Subject: [PATCH 17/26] Fix payment_link_config --- crates/diesel_models/src/payment_intent.rs | 6 +++--- .../src/payments/payment_intent.rs | 8 ++++++-- .../src/core/payments/operations/payment_update_intent.rs | 4 ++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 5085e98de9fe..fc9d0fd2fa84 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -140,7 +140,8 @@ pub struct PaymentIntent { pub psd2_sca_exemption_type: Option, } -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, diesel::AsExpression, PartialEq)] +#[diesel(sql_type = diesel::sql_types::Jsonb)] pub struct PaymentLinkConfigRequestForPayments { /// custom theme for the payment link pub theme: Option, @@ -530,8 +531,7 @@ pub struct PaymentIntentUpdateInternal { pub connector_metadata: Option, pub feature_metadata: Option, pub enable_payment_link: Option, - // TODO: Check this type - // pub payment_link_config: Option, + pub payment_link_config: Option, pub request_incremental_authorization: Option, pub session_expiry: Option, pub frm_metadata: Option, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index a1e8e8c74be3..801ca45576b4 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -157,8 +157,7 @@ pub struct PaymentIntentUpdateFields { pub connector_metadata: Option, pub feature_metadata: Option, pub enable_payment_link: Option, - // TODO: check how to use this - // payment_link_config: Box>, + pub payment_link_config: Option, pub request_incremental_authorization: Option, pub session_expiry: Option, pub frm_metadata: Option, @@ -387,6 +386,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { connector_metadata: None, feature_metadata: None, enable_payment_link: None, + payment_link_config: None, request_incremental_authorization: None, session_expiry: None, frm_metadata: None, @@ -422,6 +422,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { connector_metadata: None, feature_metadata: None, enable_payment_link: None, + payment_link_config: None, request_incremental_authorization: None, session_expiry: None, frm_metadata: None, @@ -456,6 +457,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { connector_metadata: None, feature_metadata: None, enable_payment_link: None, + payment_link_config: None, request_incremental_authorization: None, session_expiry: None, frm_metadata: None, @@ -488,6 +490,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { connector_metadata, feature_metadata, enable_payment_link, + payment_link_config, request_incremental_authorization, session_expiry, frm_metadata, @@ -528,6 +531,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { connector_metadata, feature_metadata, enable_payment_link: enable_payment_link.map(|val| val.as_bool()), + payment_link_config, request_incremental_authorization, session_expiry, frm_metadata, diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index e065ff7f1ff5..1432481be7fa 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -261,6 +261,9 @@ impl GetTracker, PaymentsUpda capture_method: capture_method.unwrap_or(payment_intent.capture_method), authentication_type: authentication_type.unwrap_or(payment_intent.authentication_type), enable_payment_link: enable_payment_link.unwrap_or(payment_intent.enable_payment_link), + payment_link_config: payment_link_config + .map(ApiModelToDieselModelConvertor::convert_from) + .or(payment_intent.payment_link_config), apply_mit_exemption: apply_mit_exemption.unwrap_or(payment_intent.apply_mit_exemption), customer_present: customer_present.unwrap_or(payment_intent.customer_present), routing_algorithm_id: routing_algorithm_id.or(payment_intent.routing_algorithm_id), @@ -337,6 +340,7 @@ impl UpdateTracker, PaymentsUpdateIn connector_metadata: intent.connector_metadata, feature_metadata: intent.feature_metadata, enable_payment_link: Some(intent.enable_payment_link), + payment_link_config: intent.payment_link_config, request_incremental_authorization: Some(intent.request_incremental_authorization), session_expiry: Some(intent.session_expiry), frm_metadata: intent.frm_metadata, From 8303b58cb21938c66a8e46e9f7e4ed33ad515a6c Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 15:20:50 +0530 Subject: [PATCH 18/26] Import instead of specifying super:: --- .../src/payments/payment_intent.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 801ca45576b4..d8e3a634da84 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -25,10 +25,11 @@ use time::PrimitiveDateTime; #[cfg(all(feature = "v1", feature = "olap"))] use super::payment_attempt::PaymentAttempt; -use super::PaymentIntent; +use super::{PaymentIntent, SurchargeCalculationOverride, TaxCalculationOverride}; #[cfg(feature = "v2")] use crate::ApiModelToDieselModelConvertor; use crate::{ + address::Address, behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, @@ -136,15 +137,15 @@ pub struct PaymentIntentUpdateFields { pub shipping_cost: Option, // TODO: Check how to handle this // tax_details: Option, - pub skip_external_tax_calculation: Option, - pub skip_surcharge_calculation: Option, + pub skip_external_tax_calculation: Option, + pub skip_surcharge_calculation: Option, pub surcharge_amount: Option, pub tax_on_surcharge: Option, pub routing_algorithm_id: Option, pub capture_method: Option, pub authentication_type: Option, - pub billing_address: Option>, - pub shipping_address: Option>, + pub billing_address: Option>, + pub shipping_address: Option>, pub customer_present: Option, pub description: Option, pub return_url: Option, @@ -1379,10 +1380,10 @@ impl behaviour::Conversion for PaymentIntent { tax_on_surcharge: storage_model.tax_on_surcharge, shipping_cost: storage_model.shipping_cost, tax_details: storage_model.tax_details, - skip_external_tax_calculation: super::TaxCalculationOverride::from( + skip_external_tax_calculation: TaxCalculationOverride::from( storage_model.skip_external_tax_calculation, ), - skip_surcharge_calculation: super::SurchargeCalculationOverride::from( + skip_surcharge_calculation: SurchargeCalculationOverride::from( storage_model.surcharge_applicable, ), amount_captured: storage_model.amount_captured, From e6dc33a08a681a237d0d9a0c725c63d00aabc31b Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 15:28:15 +0530 Subject: [PATCH 19/26] Remove #[serde(default)] from Options --- crates/api_models/src/payments.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ff6811efb61e..174751cbe698 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -551,7 +551,7 @@ pub struct AmountDetails { pub struct AmountDetailsUpdate { /// The payment amount. Amount for the payment in the lowest denomination of the currency, (i.e) in cents for USD denomination, in yen for JPY denomination etc. E.g., Pass 100 to charge $1.00 and 1 for 1¥ since ¥ is a zero-decimal currency. Read more about [the Decimal and Non-Decimal Currencies](https://github.com/juspay/hyperswitch/wiki/Decimal-and-Non%E2%80%90Decimal-Currencies) #[schema(value_type = Option, example = 6540)] - #[serde(default, deserialize_with = "amount::deserialize_option")] + #[serde(deserialize_with = "amount::deserialize_option")] order_amount: Option, /// The currency of the order #[schema(example = "USD", value_type = Option)] @@ -561,11 +561,9 @@ pub struct AmountDetailsUpdate { /// Tax amount related to the order. This will be calculated by the external tax provider order_tax_amount: Option, /// The action to whether calculate tax by calling external tax provider or not - #[serde(default)] #[schema(value_type = Option)] skip_external_tax_calculation: Option, /// The action to whether calculate surcharge or not - #[serde(default)] #[schema(value_type = Option)] skip_surcharge_calculation: Option, /// The surcharge amount to be added to the order, collected from the merchant From 9508ce98a7c9b72f63e48c1d0da3c6c8f8726076 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 15:39:19 +0530 Subject: [PATCH 20/26] Update 'PresenceOfCustomerDuringPayment' description and change to snake case --- api-reference-v2/openapi_spec.json | 6 +++--- crates/api_models/src/payments.rs | 12 ++++++------ crates/common_enums/src/enums.rs | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index fddefce86d3b..f95bc6ba06d8 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -17453,10 +17453,10 @@ }, "PresenceOfCustomerDuringPayment": { "type": "string", - "description": "Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment.", + "description": "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.", "enum": [ - "Present", - "Absent" + "present", + "absent" ] }, "PrimaryBusinessDetails": { diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 174751cbe698..7a1b415ba05e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -156,8 +156,8 @@ pub struct PaymentsCreateIntentRequest { #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] pub customer_id: Option, - /// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. - #[schema(example = true, value_type = Option)] + /// 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)] pub customer_present: Option, /// A description for the payment @@ -319,8 +319,8 @@ pub struct PaymentsUpdateIntentRequest { /// The shipping address for the payment pub shipping: Option
, - /// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. - #[schema(example = true, value_type = Option)] + /// 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)] pub customer_present: Option, /// A description for the payment @@ -445,8 +445,8 @@ pub struct PaymentsIntentResponse { #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] pub customer_id: Option, - /// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. - #[schema(example = true, value_type = PresenceOfCustomerDuringPayment)] + /// 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 = PresenceOfCustomerDuringPayment)] pub customer_present: common_enums::PresenceOfCustomerDuringPayment, /// A description for the payment diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 781b5e3710a7..d85d9245f5a3 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3317,8 +3317,9 @@ pub enum MitExemptionRequest { Skip, } -/// Set to true to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be false when merchant's doing merchant initiated payments and customer is not present while doing the payment. +/// 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. #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default, ToSchema)] +#[serde(rename_all = "snake_case")] pub enum PresenceOfCustomerDuringPayment { /// Customer is present during the payment. This is the default value #[default] From 9857110ae0367d4426051358c8a6b6669cbb014f Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 16:24:56 +0530 Subject: [PATCH 21/26] Remove redundant domain types --- crates/api_models/src/payments.rs | 8 +- crates/common_enums/src/enums.rs | 44 ++++++++- crates/hyperswitch_domain_models/src/lib.rs | 28 +----- .../hyperswitch_domain_models/src/payments.rs | 92 +++---------------- .../src/payments/payment_intent.rs | 10 +- .../router/src/core/payments/transformers.rs | 82 ++--------------- 6 files changed, 73 insertions(+), 191 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 7a1b415ba05e..7f0e9917d229 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -671,10 +671,10 @@ impl AmountDetails { self.order_tax_amount } pub fn skip_external_tax_calculation(&self) -> common_enums::TaxCalculationOverride { - self.skip_external_tax_calculation.clone() + self.skip_external_tax_calculation } pub fn skip_surcharge_calculation(&self) -> common_enums::SurchargeCalculationOverride { - self.skip_surcharge_calculation.clone() + self.skip_surcharge_calculation } pub fn surcharge_amount(&self) -> Option { self.surcharge_amount @@ -699,10 +699,10 @@ impl AmountDetailsUpdate { self.order_tax_amount } pub fn skip_external_tax_calculation(&self) -> Option { - self.skip_external_tax_calculation.clone() + self.skip_external_tax_calculation } pub fn skip_surcharge_calculation(&self) -> Option { - self.skip_surcharge_calculation.clone() + self.skip_surcharge_calculation } pub fn surcharge_amount(&self) -> Option { self.surcharge_amount diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index d85d9245f5a3..e9a7bbec4b30 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3328,7 +3328,9 @@ pub enum PresenceOfCustomerDuringPayment { Absent, } -#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default, ToSchema)] +#[derive( + Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default, ToSchema, +)] pub enum TaxCalculationOverride { /// Skip calling the external tax provider #[default] @@ -3337,7 +3339,27 @@ pub enum TaxCalculationOverride { Calculate, } -#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default, ToSchema)] +impl From> for TaxCalculationOverride { + fn from(value: Option) -> Self { + match value { + Some(true) => Self::Calculate, + _ => Self::Skip, + } + } +} + +impl TaxCalculationOverride { + pub fn as_bool(self) -> bool { + match self { + Self::Skip => false, + Self::Calculate => true, + } + } +} + +#[derive( + Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, Default, ToSchema, +)] pub enum SurchargeCalculationOverride { /// Skip calculating surcharge #[default] @@ -3346,6 +3368,24 @@ pub enum SurchargeCalculationOverride { Calculate, } +impl From> for SurchargeCalculationOverride { + fn from(value: Option) -> Self { + match value { + Some(true) => Self::Calculate, + _ => Self::Skip, + } + } +} + +impl SurchargeCalculationOverride { + pub fn as_bool(self) -> bool { + match self { + Self::Skip => false, + Self::Calculate => true, + } + } +} + /// Connector Mandate Status #[derive( Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, strum::Display, diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index d94e921c120b..31732e132868 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -302,12 +302,8 @@ impl From for payments::AmountDetails { payment_method_type: None, } }), - skip_external_tax_calculation: payments::TaxCalculationOverride::from( - amount_details.skip_external_tax_calculation(), - ), - skip_surcharge_calculation: payments::SurchargeCalculationOverride::from( - amount_details.skip_surcharge_calculation(), - ), + skip_external_tax_calculation: amount_details.skip_external_tax_calculation(), + skip_surcharge_calculation: amount_details.skip_surcharge_calculation(), surcharge_amount: amount_details.surcharge_amount(), tax_on_surcharge: amount_details.tax_on_surcharge(), // We will not receive this in the request. This will be populated after calling the connector / processor @@ -315,23 +311,3 @@ impl From for payments::AmountDetails { } } } - -#[cfg(feature = "v2")] -impl From for payments::SurchargeCalculationOverride { - fn from(surcharge_calculation_override: common_enums::SurchargeCalculationOverride) -> Self { - match surcharge_calculation_override { - common_enums::SurchargeCalculationOverride::Calculate => Self::Calculate, - common_enums::SurchargeCalculationOverride::Skip => Self::Skip, - } - } -} - -#[cfg(feature = "v2")] -impl From for payments::TaxCalculationOverride { - fn from(tax_calculation_override: common_enums::TaxCalculationOverride) -> Self { - match tax_calculation_override { - common_enums::TaxCalculationOverride::Calculate => Self::Calculate, - common_enums::TaxCalculationOverride::Skip => Self::Skip, - } - } -} diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index f97d18f3479c..bd1014ce3960 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -157,64 +157,6 @@ impl PaymentIntent { } } -#[cfg(feature = "v2")] -#[derive(Clone, Debug, PartialEq, Copy, serde::Serialize)] -pub enum TaxCalculationOverride { - /// Skip calling the external tax provider - Skip, - /// Calculate tax by calling the external tax provider - Calculate, -} - -#[cfg(feature = "v2")] -#[derive(Clone, Debug, PartialEq, Copy, serde::Serialize)] -pub enum SurchargeCalculationOverride { - /// Skip calculating surcharge - Skip, - /// Calculate surcharge - Calculate, -} - -#[cfg(feature = "v2")] -impl From> for TaxCalculationOverride { - fn from(value: Option) -> Self { - match value { - Some(true) => Self::Calculate, - _ => Self::Skip, - } - } -} - -#[cfg(feature = "v2")] -impl TaxCalculationOverride { - fn as_bool(self) -> bool { - match self { - Self::Skip => false, - Self::Calculate => true, - } - } -} - -#[cfg(feature = "v2")] -impl From> for SurchargeCalculationOverride { - fn from(value: Option) -> Self { - match value { - Some(true) => Self::Calculate, - _ => Self::Skip, - } - } -} - -#[cfg(feature = "v2")] -impl SurchargeCalculationOverride { - fn as_bool(self) -> bool { - match self { - Self::Skip => false, - Self::Calculate => true, - } - } -} - #[cfg(feature = "v2")] #[derive(Clone, Debug, PartialEq, serde::Serialize)] pub struct AmountDetails { @@ -227,9 +169,9 @@ pub struct AmountDetails { /// Tax details related to the order. This will be calculated by the external tax provider pub tax_details: Option, /// The action to whether calculate tax by calling external tax provider or not - pub skip_external_tax_calculation: TaxCalculationOverride, + pub skip_external_tax_calculation: common_enums::TaxCalculationOverride, /// The action to whether calculate surcharge or not - pub skip_surcharge_calculation: SurchargeCalculationOverride, + pub skip_surcharge_calculation: common_enums::SurchargeCalculationOverride, /// The surcharge amount to be added to the order, collected from the merchant pub surcharge_amount: Option, /// tax on surcharge amount @@ -243,18 +185,12 @@ pub struct AmountDetails { impl AmountDetails { /// Get the action to whether calculate surcharge or not as a boolean value fn get_surcharge_action_as_bool(&self) -> bool { - match self.skip_surcharge_calculation { - SurchargeCalculationOverride::Skip => false, - SurchargeCalculationOverride::Calculate => true, - } + self.skip_surcharge_calculation.as_bool() } /// Get the action to whether calculate external tax or not as a boolean value fn get_external_tax_action_as_bool(&self) -> bool { - match self.skip_external_tax_calculation { - TaxCalculationOverride::Skip => false, - TaxCalculationOverride::Calculate => true, - } + self.skip_external_tax_calculation.as_bool() } /// Calculate the net amount for the order @@ -272,20 +208,22 @@ impl AmountDetails { let net_amount = self.calculate_net_amount(); let surcharge_amount = match self.skip_surcharge_calculation { - SurchargeCalculationOverride::Skip => self.surcharge_amount, - SurchargeCalculationOverride::Calculate => None, + common_enums::SurchargeCalculationOverride::Skip => self.surcharge_amount, + common_enums::SurchargeCalculationOverride::Calculate => None, }; let tax_on_surcharge = match self.skip_surcharge_calculation { - SurchargeCalculationOverride::Skip => self.tax_on_surcharge, - SurchargeCalculationOverride::Calculate => None, + common_enums::SurchargeCalculationOverride::Skip => self.tax_on_surcharge, + common_enums::SurchargeCalculationOverride::Calculate => None, }; let order_tax_amount = match self.skip_external_tax_calculation { - TaxCalculationOverride::Skip => self.tax_details.as_ref().and_then(|tax_details| { - tax_details.get_tax_amount(confirm_intent_request.payment_method_subtype) - }), - TaxCalculationOverride::Calculate => None, + common_enums::TaxCalculationOverride::Skip => { + self.tax_details.as_ref().and_then(|tax_details| { + tax_details.get_tax_amount(confirm_intent_request.payment_method_subtype) + }) + } + common_enums::TaxCalculationOverride::Calculate => None, }; payment_attempt::AttemptAmountDetails { @@ -311,11 +249,9 @@ impl AmountDetails { tax_details: self.tax_details, skip_external_tax_calculation: req .skip_external_tax_calculation() - .map(Into::into) .unwrap_or(self.skip_external_tax_calculation), skip_surcharge_calculation: req .skip_surcharge_calculation() - .map(Into::into) .unwrap_or(self.skip_surcharge_calculation), surcharge_amount: req.surcharge_amount().or(self.surcharge_amount), tax_on_surcharge: req.tax_on_surcharge().or(self.tax_on_surcharge), diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index d8e3a634da84..18866dba07a4 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -25,7 +25,7 @@ use time::PrimitiveDateTime; #[cfg(all(feature = "v1", feature = "olap"))] use super::payment_attempt::PaymentAttempt; -use super::{PaymentIntent, SurchargeCalculationOverride, TaxCalculationOverride}; +use super::PaymentIntent; #[cfg(feature = "v2")] use crate::ApiModelToDieselModelConvertor; use crate::{ @@ -137,8 +137,8 @@ pub struct PaymentIntentUpdateFields { pub shipping_cost: Option, // TODO: Check how to handle this // tax_details: Option, - pub skip_external_tax_calculation: Option, - pub skip_surcharge_calculation: Option, + pub skip_external_tax_calculation: Option, + pub skip_surcharge_calculation: Option, pub surcharge_amount: Option, pub tax_on_surcharge: Option, pub routing_algorithm_id: Option, @@ -1380,10 +1380,10 @@ impl behaviour::Conversion for PaymentIntent { tax_on_surcharge: storage_model.tax_on_surcharge, shipping_cost: storage_model.shipping_cost, tax_details: storage_model.tax_details, - skip_external_tax_calculation: TaxCalculationOverride::from( + skip_external_tax_calculation: common_enums::TaxCalculationOverride::from( storage_model.skip_external_tax_calculation, ), - skip_surcharge_calculation: SurchargeCalculationOverride::from( + skip_surcharge_calculation: common_enums::SurchargeCalculationOverride::from( storage_model.surcharge_applicable, ), amount_captured: storage_model.amount_captured, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 9e0557d5b3eb..416bfebf6c60 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -3513,12 +3513,8 @@ impl currency: intent_amount_details.currency, shipping_cost: attempt_amount_details.shipping_cost, order_tax_amount: attempt_amount_details.order_tax_amount, - skip_external_tax_calculation: common_enums::TaxCalculationOverride::foreign_from( - intent_amount_details.skip_external_tax_calculation, - ), - skip_surcharge_calculation: common_enums::SurchargeCalculationOverride::foreign_from( - intent_amount_details.skip_surcharge_calculation, - ), + skip_external_tax_calculation: intent_amount_details.skip_external_tax_calculation, + skip_surcharge_calculation: intent_amount_details.skip_surcharge_calculation, surcharge_amount: attempt_amount_details.surcharge_amount, tax_on_surcharge: attempt_amount_details.tax_on_surcharge, net_amount: attempt_amount_details.net_amount, @@ -3556,12 +3552,8 @@ impl .tax_details .as_ref() .and_then(|tax_details| tax_details.get_default_tax_amount())), - skip_external_tax_calculation: common_enums::TaxCalculationOverride::foreign_from( - intent_amount_details.skip_external_tax_calculation, - ), - skip_surcharge_calculation: common_enums::SurchargeCalculationOverride::foreign_from( - intent_amount_details.skip_surcharge_calculation, - ), + skip_external_tax_calculation: intent_amount_details.skip_external_tax_calculation, + skip_surcharge_calculation: intent_amount_details.skip_surcharge_calculation, surcharge_amount: attempt_amount_details .and_then(|attempt| attempt.surcharge_amount) .or(intent_amount_details.surcharge_amount), @@ -3616,76 +3608,14 @@ impl ForeignFrom order_tax_amount: amount_details.tax_details.and_then(|tax_details| { tax_details.default.map(|default| default.order_tax_amount) }), - skip_external_tax_calculation: common_enums::TaxCalculationOverride::foreign_from( - amount_details.skip_external_tax_calculation, - ), - skip_surcharge_calculation: common_enums::SurchargeCalculationOverride::foreign_from( - amount_details.skip_surcharge_calculation, - ), + skip_external_tax_calculation: amount_details.skip_external_tax_calculation, + skip_surcharge_calculation: amount_details.skip_surcharge_calculation, surcharge_amount: amount_details.surcharge_amount, tax_on_surcharge: amount_details.tax_on_surcharge, } } } -#[cfg(feature = "v2")] -impl ForeignFrom - for hyperswitch_domain_models::payments::TaxCalculationOverride -{ - fn foreign_from(tax_calculation_override: common_enums::TaxCalculationOverride) -> Self { - match tax_calculation_override { - common_enums::TaxCalculationOverride::Calculate => Self::Calculate, - common_enums::TaxCalculationOverride::Skip => Self::Skip, - } - } -} - -#[cfg(feature = "v2")] -impl ForeignFrom - for common_enums::TaxCalculationOverride -{ - fn foreign_from( - tax_calculation_override: hyperswitch_domain_models::payments::TaxCalculationOverride, - ) -> Self { - match tax_calculation_override { - hyperswitch_domain_models::payments::TaxCalculationOverride::Calculate => { - Self::Calculate - } - hyperswitch_domain_models::payments::TaxCalculationOverride::Skip => Self::Skip, - } - } -} - -#[cfg(feature = "v2")] -impl ForeignFrom - for hyperswitch_domain_models::payments::SurchargeCalculationOverride -{ - fn foreign_from( - surcharge_calculation_override: common_enums::SurchargeCalculationOverride, - ) -> Self { - match surcharge_calculation_override { - common_enums::SurchargeCalculationOverride::Calculate => Self::Calculate, - common_enums::SurchargeCalculationOverride::Skip => Self::Skip, - } - } -} - -#[cfg(feature = "v2")] -impl ForeignFrom - for common_enums::SurchargeCalculationOverride -{ - fn foreign_from( - surcharge_calculation_override: hyperswitch_domain_models::payments::SurchargeCalculationOverride, - ) -> Self { - match surcharge_calculation_override { - hyperswitch_domain_models::payments::SurchargeCalculationOverride::Calculate => { - Self::Calculate - } - hyperswitch_domain_models::payments::SurchargeCalculationOverride::Skip => Self::Skip, - } - } -} - #[cfg(feature = "v2")] impl ForeignFrom for diesel_models::PaymentLinkConfigRequestForPayments From 89f0a4a0d7f339efcaf74721564ccec87535386b Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 16:25:07 +0530 Subject: [PATCH 22/26] Fix mintlify docs --- .../api-reference/payments/payments--update-intent.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-reference-v2/api-reference/payments/payments--update-intent.mdx b/api-reference-v2/api-reference/payments/payments--update-intent.mdx index 4c295b3f8347..3ef58f4db721 100644 --- a/api-reference-v2/api-reference/payments/payments--update-intent.mdx +++ b/api-reference-v2/api-reference/payments/payments--update-intent.mdx @@ -1,3 +1,3 @@ --- -openapi: post /v2/payments/{id}/update-intent +openapi: put /v2/payments/{id}/update-intent --- \ No newline at end of file From a1220703a6db2f5ebf87e74f4ee4a2eabb040b25 Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Wed, 4 Dec 2024 19:52:21 +0530 Subject: [PATCH 23/26] Handle tax_details --- crates/api_models/src/payments.rs | 2 +- crates/diesel_models/src/payment_intent.rs | 2 +- crates/hyperswitch_domain_models/src/payments.rs | 8 +++++++- .../src/payments/payment_intent.rs | 8 ++++++-- .../src/core/payments/operations/payment_update_intent.rs | 1 + 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 9bc7964cee02..36d3642dde62 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -555,7 +555,7 @@ pub struct AmountDetails { pub struct AmountDetailsUpdate { /// The payment amount. Amount for the payment in the lowest denomination of the currency, (i.e) in cents for USD denomination, in yen for JPY denomination etc. E.g., Pass 100 to charge $1.00 and 1 for 1¥ since ¥ is a zero-decimal currency. Read more about [the Decimal and Non-Decimal Currencies](https://github.com/juspay/hyperswitch/wiki/Decimal-and-Non%E2%80%90Decimal-Currencies) #[schema(value_type = Option, example = 6540)] - #[serde(deserialize_with = "amount::deserialize_option")] + #[serde(default, deserialize_with = "amount::deserialize_option")] order_amount: Option, /// The currency of the order #[schema(example = "USD", value_type = Option)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index fc9d0fd2fa84..0164e27e7bc3 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -510,6 +510,7 @@ pub struct PaymentIntentUpdateInternal { pub amount: Option, pub currency: Option, pub shipping_cost: Option, + pub tax_details: Option, pub skip_external_tax_calculation: Option, pub surcharge_applicable: Option, pub surcharge_amount: Option, @@ -535,7 +536,6 @@ pub struct PaymentIntentUpdateInternal { pub request_incremental_authorization: Option, pub session_expiry: Option, pub frm_metadata: Option, - // Setting it to common_enums::External3dsAuthenticationRequest gives error on `AsChangeset` derivation pub request_external_three_ds_authentication: Option, pub updated_by: String, } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index a3235561308f..9e6477fa26ba 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -246,7 +246,13 @@ impl AmountDetails { .into(), currency: req.currency().unwrap_or(self.currency), shipping_cost: req.shipping_cost().or(self.shipping_cost), - tax_details: self.tax_details, + tax_details: req + .order_tax_amount() + .map(|order_tax_amount| TaxDetails { + default: Some(diesel_models::DefaultTax { order_tax_amount }), + payment_method_type: None, + }) + .or(self.tax_details), skip_external_tax_calculation: req .skip_external_tax_calculation() .unwrap_or(self.skip_external_tax_calculation), diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 18866dba07a4..740d76fbd058 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -135,8 +135,7 @@ pub struct PaymentIntentUpdateFields { pub amount: Option, pub currency: Option, pub shipping_cost: Option, - // TODO: Check how to handle this - // tax_details: Option, + pub tax_details: Option, pub skip_external_tax_calculation: Option, pub skip_surcharge_calculation: Option, pub surcharge_amount: Option, @@ -366,6 +365,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount: None, currency: None, shipping_cost: None, + tax_details: None, skip_external_tax_calculation: None, surcharge_applicable: None, surcharge_amount: None, @@ -402,6 +402,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount: None, currency: None, shipping_cost: None, + tax_details: None, skip_external_tax_calculation: None, surcharge_applicable: None, surcharge_amount: None, @@ -437,6 +438,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount: None, currency: None, shipping_cost: None, + tax_details: None, skip_external_tax_calculation: None, surcharge_applicable: None, surcharge_amount: None, @@ -470,6 +472,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount, currency, shipping_cost, + tax_details, skip_external_tax_calculation, skip_surcharge_calculation, surcharge_amount, @@ -506,6 +509,7 @@ impl From for diesel_models::PaymentIntentUpdateInternal { amount, currency, shipping_cost, + tax_details, skip_external_tax_calculation: skip_external_tax_calculation .map(|val| val.as_bool()), surcharge_applicable: skip_surcharge_calculation.map(|val| val.as_bool()), diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 1432481be7fa..7e2711f9a857 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -348,6 +348,7 @@ impl UpdateTracker, PaymentsUpdateIn intent.request_external_three_ds_authentication, ), updated_by: intent.updated_by, + tax_details: intent.amount_details.tax_details, })); let new_payment_intent = db From 914269b424fb5b4aa3c4154efeac2775322f98ef Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Thu, 5 Dec 2024 13:30:44 +0530 Subject: [PATCH 24/26] Fix Clippy --- .../hyperswitch_domain_models/src/payments/payment_intent.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 740d76fbd058..91b538b91998 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -27,9 +27,10 @@ use time::PrimitiveDateTime; use super::payment_attempt::PaymentAttempt; use super::PaymentIntent; #[cfg(feature = "v2")] +use crate::address::Address; +#[cfg(feature = "v2")] use crate::ApiModelToDieselModelConvertor; use crate::{ - address::Address, behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, From c11807807730349a5440a8a26bb43f6d9195ff9c Mon Sep 17 00:00:00 2001 From: Anurag Thakur Date: Thu, 5 Dec 2024 13:43:44 +0530 Subject: [PATCH 25/26] Remove enable_payment_link from update request --- api-reference-v2/openapi_spec.json | 8 -------- crates/api_models/src/payments.rs | 4 ---- crates/diesel_models/src/payment_intent.rs | 1 - .../src/payments/payment_intent.rs | 8 -------- .../src/core/payments/operations/payment_update_intent.rs | 3 --- 5 files changed, 24 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 58cbe4e4f353..2bb365046319 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -16169,14 +16169,6 @@ ], "nullable": true }, - "enable_payment_link": { - "allOf": [ - { - "$ref": "#/components/schemas/EnablePaymentLinkRequest" - } - ], - "nullable": true - }, "payment_link_config": { "allOf": [ { diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 36d3642dde62..6a7f7148bd3a 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -367,10 +367,6 @@ pub struct PaymentsUpdateIntentRequest { #[schema(value_type = Option)] pub feature_metadata: Option, - /// Whether to generate the payment link for this payment or not (if applicable) - #[schema(value_type = Option)] - pub enable_payment_link: Option, - /// Configure a custom payment link for the particular payment #[schema(value_type = Option)] pub payment_link_config: Option, diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 0164e27e7bc3..b29da50d0a7d 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -531,7 +531,6 @@ pub struct PaymentIntentUpdateInternal { pub metadata: Option, pub connector_metadata: Option, pub feature_metadata: Option, - pub enable_payment_link: Option, pub payment_link_config: Option, pub request_incremental_authorization: Option, pub session_expiry: Option, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 91b538b91998..9b6b6fe55720 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -28,8 +28,6 @@ use super::payment_attempt::PaymentAttempt; use super::PaymentIntent; #[cfg(feature = "v2")] use crate::address::Address; -#[cfg(feature = "v2")] -use crate::ApiModelToDieselModelConvertor; use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, @@ -157,7 +155,6 @@ pub struct PaymentIntentUpdateFields { pub metadata: Option, pub connector_metadata: Option, pub feature_metadata: Option, - pub enable_payment_link: Option, pub payment_link_config: Option, pub request_incremental_authorization: Option, pub session_expiry: Option, @@ -387,7 +384,6 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: None, connector_metadata: None, feature_metadata: None, - enable_payment_link: None, payment_link_config: None, request_incremental_authorization: None, session_expiry: None, @@ -424,7 +420,6 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: None, connector_metadata: None, feature_metadata: None, - enable_payment_link: None, payment_link_config: None, request_incremental_authorization: None, session_expiry: None, @@ -460,7 +455,6 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata: None, connector_metadata: None, feature_metadata: None, - enable_payment_link: None, payment_link_config: None, request_incremental_authorization: None, session_expiry: None, @@ -494,7 +488,6 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata, connector_metadata, feature_metadata, - enable_payment_link, payment_link_config, request_incremental_authorization, session_expiry, @@ -536,7 +529,6 @@ impl From for diesel_models::PaymentIntentUpdateInternal { metadata, connector_metadata, feature_metadata, - enable_payment_link: enable_payment_link.map(|val| val.as_bool()), payment_link_config, request_incremental_authorization, session_expiry, diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index 7e2711f9a857..f8ce03d558e9 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -164,7 +164,6 @@ impl GetTracker, PaymentsUpda metadata, connector_metadata, feature_metadata, - enable_payment_link, payment_link_config, request_incremental_authorization, session_expiry, @@ -260,7 +259,6 @@ impl GetTracker, PaymentsUpda .attach_printable("Unable to decode shipping address")?, capture_method: capture_method.unwrap_or(payment_intent.capture_method), authentication_type: authentication_type.unwrap_or(payment_intent.authentication_type), - enable_payment_link: enable_payment_link.unwrap_or(payment_intent.enable_payment_link), payment_link_config: payment_link_config .map(ApiModelToDieselModelConvertor::convert_from) .or(payment_intent.payment_link_config), @@ -339,7 +337,6 @@ impl UpdateTracker, PaymentsUpdateIn metadata: intent.metadata, connector_metadata: intent.connector_metadata, feature_metadata: intent.feature_metadata, - enable_payment_link: Some(intent.enable_payment_link), payment_link_config: intent.payment_link_config, request_incremental_authorization: Some(intent.request_incremental_authorization), session_expiry: Some(intent.session_expiry), From c4377b95b5ec7b72977951c0643781ca60327801 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:25:51 +0000 Subject: [PATCH 26/26] chore: run formatter --- crates/common_enums/src/enums.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index b687c356046e..6c38172e5406 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3343,7 +3343,6 @@ pub enum PresenceOfCustomerDuringPayment { Absent, } - impl From for TransactionType { fn from(connector_type: ConnectorType) -> Self { match connector_type {