Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(payment_v2): implement payments sync #6464

Merged
merged 32 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
110a041
feat: implement payments retrieve for v2
Narayanbhat166 Oct 29, 2024
579cec1
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Oct 29, 2024
d098629
refactor: remove direct attempt access in call_connector_service
Narayanbhat166 Oct 29, 2024
186136c
chore: run formatter
hyperswitch-bot[bot] Oct 29, 2024
a5d0526
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Oct 29, 2024
d040fbc
chore: rename PaymentStatusRequest to PaymentRetrieveRequest
Narayanbhat166 Oct 30, 2024
c5e4df2
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Oct 30, 2024
ef71de3
chore: cargo clippy
Narayanbhat166 Oct 30, 2024
e50a071
refactor: use separate update enums for sync update
Narayanbhat166 Oct 30, 2024
0643a08
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Oct 31, 2024
575ef18
chore: run formatter
hyperswitch-bot[bot] Oct 31, 2024
0e93355
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Oct 31, 2024
ebe079f
chore: cargo clippy
Narayanbhat166 Oct 31, 2024
ba7daba
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Nov 4, 2024
1c5212c
refactor: address PR comments
Narayanbhat166 Nov 4, 2024
5709519
chore: run formatter
hyperswitch-bot[bot] Nov 4, 2024
56dab7e
refactor: move updatable trackers objects to a trait on router_data
Narayanbhat166 Nov 4, 2024
95b51f4
chore: cargo clippy
Narayanbhat166 Nov 4, 2024
940fbe0
refactor: change payments retrieve to get api and added jwt auth
Narayanbhat166 Nov 4, 2024
e832d29
refactor: allow force sync for PartiallyCapturedAndCapturable status
Narayanbhat166 Nov 4, 2024
f8ed350
chore: add a todo comment
Narayanbhat166 Nov 4, 2024
f26679e
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Nov 5, 2024
252bd8e
chore: cargo cilipy
Narayanbhat166 Nov 5, 2024
002b060
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Nov 5, 2024
b85b6f6
refactor: remove client secret auth and add header auth
Narayanbhat166 Nov 5, 2024
62c4479
chore: remove unused struct
Narayanbhat166 Nov 5, 2024
3248d77
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Nov 6, 2024
76d872a
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Nov 8, 2024
cc54284
chore: cargo clippy_v2
Narayanbhat166 Nov 8, 2024
d050efb
Merge branch 'main' into payment_sync_v2
Narayanbhat166 Nov 9, 2024
301bd59
refactor: rename authentication data to redirect form
Narayanbhat166 Nov 9, 2024
ee63776
chore: run formatter
hyperswitch-bot[bot] Nov 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 6 additions & 47 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -14314,6 +14314,7 @@
"type": "object",
"required": [
"id",
"status",
"amount_details",
"client_secret",
"capture_method",
Expand All @@ -14331,6 +14332,9 @@
"type": "string",
"description": "Global Payment Id for the payment"
},
"status": {
"$ref": "#/components/schemas/IntentStatus"
},
"amount_details": {
"$ref": "#/components/schemas/AmountDetailsResponse"
},
Expand Down Expand Up @@ -15049,56 +15053,11 @@
},
"PaymentsRetrieveRequest": {
"type": "object",
"required": [
"resource_id",
"force_sync"
],
"description": "Request for Payment Status",
"properties": {
"resource_id": {
"type": "string",
"description": "The type of ID (ex: payment intent id, payment attempt id or connector txn id)"
},
"merchant_id": {
"type": "string",
"description": "The identifier for the Merchant Account.",
"nullable": true
},
"force_sync": {
"type": "boolean",
"description": "Decider to enable or disable the connector call for retrieve request"
},
"param": {
"type": "string",
"description": "The parameters passed to a retrieve request",
"nullable": true
},
"connector": {
"type": "string",
"description": "The name of the connector",
"nullable": true
},
"merchant_connector_details": {
"allOf": [
{
"$ref": "#/components/schemas/MerchantConnectorDetailsWrap"
}
],
"nullable": true
},
"client_secret": {
"type": "string",
"description": "This is a token which expires after 15 minutes, used from the client to authenticate and create sessions from the SDK",
"nullable": true
},
"expand_captures": {
"type": "boolean",
"description": "If enabled provides list of captures linked to latest attempt",
"nullable": true
},
"expand_attempts": {
"type": "boolean",
"description": "If enabled provides list of attempts linked to payment intent",
"nullable": true
"description": "A boolean used to indicate if the payment status should be fetched from the connector\nIf this is set to true, the status will be fetched from the connector"
}
}
},
Expand Down
9 changes: 9 additions & 0 deletions crates/api_models/src/events/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ impl ApiEventMetric for PaymentsConfirmIntentResponse {
}
}

#[cfg(feature = "v2")]
impl ApiEventMetric for super::PaymentsRetrieveResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Payment {
payment_id: self.id.clone(),
})
}
}

#[cfg(feature = "v1")]
impl ApiEventMetric for PaymentsResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Expand Down
84 changes: 84 additions & 0 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ pub struct PaymentsIntentResponse {
#[schema(value_type = String)]
pub id: id_type::GlobalPaymentId,

/// The status of the payment
#[schema(value_type = IntentStatus, example = "succeeded")]
pub status: common_enums::IntentStatus,

/// The amount details for the payment
pub amount_details: AmountDetailsResponse,

Expand Down Expand Up @@ -4484,6 +4488,19 @@ pub struct PaymentsConfirmIntentRequest {
pub browser_info: Option<common_utils::types::BrowserInformation>,
}

// Serialize is implemented because, this will be serialized in the api events.
// Usually request types should not have serialize implemented.
//
/// Request for Payment Status
#[cfg(feature = "v2")]
#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)]
pub struct PaymentsRetrieveRequest {
/// A boolean used to indicate if the payment status should be fetched from the connector
/// If this is set to true, the status will be fetched from the connector
#[serde(default)]
pub force_sync: bool,
Narayanbhat166 marked this conversation as resolved.
Show resolved Hide resolved
}

/// Error details for the payment
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
Expand Down Expand Up @@ -4568,6 +4585,72 @@ pub struct PaymentsConfirmIntentResponse {
pub error: Option<ErrorDetails>,
}

// TODO: have a separate response for detailed, summarized
/// Response for Payment Intent Confirm
#[cfg(feature = "v2")]
#[derive(Debug, serde::Serialize, ToSchema)]
pub struct PaymentsRetrieveResponse {
/// Unique identifier for the payment. This ensures idempotency for multiple payments
/// that have been done by a single merchant.
#[schema(
min_length = 32,
max_length = 64,
example = "12345_pay_01926c58bc6e77c09e809964e72af8c8",
value_type = String,
)]
pub id: id_type::GlobalPaymentId,

#[schema(value_type = IntentStatus, example = "succeeded")]
pub status: api_enums::IntentStatus,

/// Amount related information for this payment and attempt
pub amount: ConfirmIntentAmountDetailsResponse,

/// The connector used for the payment
#[schema(example = "stripe")]
pub connector: Option<String>,
sai-harsha-vardhan marked this conversation as resolved.
Show resolved Hide resolved

/// It's a token used for client side verification.
#[schema(value_type = String)]
pub client_secret: common_utils::types::ClientSecret,

/// Time when the payment was created
#[schema(example = "2022-09-10T10:11:12Z")]
#[serde(with = "common_utils::custom_serde::iso8601")]
pub created: PrimitiveDateTime,

/// The payment method information provided for making a payment
#[schema(value_type = Option<PaymentMethodDataResponseWithBilling>)]
#[serde(serialize_with = "serialize_payment_method_data_response")]
pub payment_method_data: Option<PaymentMethodDataResponseWithBilling>,

/// The payment method type for this payment attempt
#[schema(value_type = Option<PaymentMethod>, example = "wallet")]
pub payment_method_type: Option<api_enums::PaymentMethod>,

#[schema(value_type = Option<PaymentMethodType>, example = "apple_pay")]
pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

/// A unique identifier for a payment provided by the connector
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_transaction_id: Option<String>,

/// reference(Identifier) to the payment at connector side
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_reference_id: Option<String>,

/// Identifier of the connector ( merchant connector account ) which was chosen to make the payment
#[schema(value_type = Option<String>)]
pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
Narayanbhat166 marked this conversation as resolved.
Show resolved Hide resolved

/// The browser information used for this payment
#[schema(value_type = Option<BrowserInformation>)]
pub browser_info: Option<common_utils::types::BrowserInformation>,

/// Error details for the payment if any
pub error: Option<ErrorDetails>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionRequest {
Expand Down Expand Up @@ -5081,6 +5164,7 @@ pub struct PaymentsResponseForm {
pub order_id: String,
}

#[cfg(feature = "v1")]
#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
pub struct PaymentsRetrieveRequest {
/// The type of ID (ex: payment intent id, payment attempt id or connector txn id)
Expand Down
22 changes: 22 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,28 @@ pub enum IntentStatus {
PartiallyCapturedAndCapturable,
}

impl IntentStatus {
/// Indicates whether the syncing with the connector should be allowed or not
pub fn should_force_sync_with_connector(&self) -> bool {
match self {
// Confirm has not happened yet
Self::RequiresConfirmation
| Self::RequiresPaymentMethod
// Once the status is success, failed or cancelled need not force sync with the connector
| Self::Succeeded
| Self::Failed
| Self::Cancelled
| Self::PartiallyCaptured
sai-harsha-vardhan marked this conversation as resolved.
Show resolved Hide resolved
| Self::RequiresCapture => false,
Self::Processing
| Self::RequiresCustomerAction
| Self::RequiresMerchantAction
| Self::PartiallyCapturedAndCapturable
=> true,
}
}
}

/// Indicates that you intend to make future payments with the payment methods used for this Payment. Providing this parameter will attach the payment method to the Customer, if present, after the Payment is confirmed and any required actions from the user are complete.
/// - On_session - Payment method saved only at hyperswitch when consent is provided by the user. CVV will asked during the returning user payment
/// - Off_session - Payment method saved at both hyperswitch and Processor when consent is provided by the user. No input is required during the returning user payment.
Expand Down
40 changes: 39 additions & 1 deletion crates/common_enums/src/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::fmt::{Display, Formatter};

use serde::{Deserialize, Serialize};

use crate::enums::{Country, CountryAlpha2, CountryAlpha3, PaymentMethod, PaymentMethodType};
use crate::enums::{
AttemptStatus, Country, CountryAlpha2, CountryAlpha3, IntentStatus, PaymentMethod,
PaymentMethodType,
};

impl Display for NumericCountryCodeParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Expand Down Expand Up @@ -2065,6 +2068,41 @@ impl super::PresenceOfCustomerDuringPayment {
}
}

impl From<AttemptStatus> for IntentStatus {
fn from(s: AttemptStatus) -> Self {
match s {
AttemptStatus::Charged | AttemptStatus::AutoRefunded => Self::Succeeded,

AttemptStatus::ConfirmationAwaited => Self::RequiresConfirmation,
AttemptStatus::PaymentMethodAwaited => Self::RequiresPaymentMethod,

AttemptStatus::Authorized => Self::RequiresCapture,
AttemptStatus::AuthenticationPending | AttemptStatus::DeviceDataCollectionPending => {
Self::RequiresCustomerAction
}
AttemptStatus::Unresolved => Self::RequiresMerchantAction,

AttemptStatus::PartialCharged => Self::PartiallyCaptured,
AttemptStatus::PartialChargedAndChargeable => Self::PartiallyCapturedAndCapturable,
AttemptStatus::Started
| AttemptStatus::AuthenticationSuccessful
| AttemptStatus::Authorizing
| AttemptStatus::CodInitiated
| AttemptStatus::VoidInitiated
| AttemptStatus::CaptureInitiated
| AttemptStatus::Pending => Self::Processing,

AttemptStatus::AuthenticationFailed
| AttemptStatus::AuthorizationFailed
| AttemptStatus::VoidFailed
| AttemptStatus::RouterDeclined
| AttemptStatus::CaptureFailed
| AttemptStatus::Failure => Self::Failed,
AttemptStatus::Voided => Self::Cancelled,
}
}
}

#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
Expand Down
10 changes: 10 additions & 0 deletions crates/common_utils/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,16 @@ impl Url {
pub fn get_string_repr(&self) -> &str {
self.0.as_str()
}

/// wrap the url::Url in Url type
pub fn wrap(url: url::Url) -> Self {
Self(url)
}

/// Get the inner url
pub fn into_inner(self) -> url::Url {
self.0
}
}

impl<DB> ToSql<sql_types::Text, DB> for Url
Expand Down
9 changes: 9 additions & 0 deletions crates/diesel_models/src/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};

#[cfg(feature = "v2")]
use crate::payment_attempt::PaymentAttemptUpdateInternal;
#[cfg(feature = "v2")]
use crate::payment_intent::PaymentIntentUpdateInternal;
use crate::{
address::{Address, AddressNew, AddressUpdateInternal},
customers::{Customer, CustomerNew, CustomerUpdateInternal},
Expand Down Expand Up @@ -108,9 +110,16 @@ impl DBOperation {
Insertable::Mandate(m) => DBResult::Mandate(Box::new(m.insert(conn).await?)),
},
Self::Update { updatable } => match *updatable {
#[cfg(feature = "v1")]
Updateable::PaymentIntentUpdate(a) => {
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?,
)),
#[cfg(feature = "v1")]
Updateable::PaymentAttemptUpdate(a) => DBResult::PaymentAttempt(Box::new(
a.orig.update_with_attempt_id(conn, a.update_data).await?,
Expand Down
Loading
Loading