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(payouts): implement KVRouterStore #3889

Merged
merged 24 commits into from
Mar 19, 2024
Merged

Conversation

kashif-m
Copy link
Contributor

@kashif-m kashif-m commented Feb 29, 2024

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

This PR includes below changes

  • implement KVRouterStore for storing payout data in either redis + postgres or postgres only
  • moving required columns for dashboard analytics to a single table
  • usage and consumption of payouts feature throughout crates

Explained in issue - #3652

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

How did you test it?

By verifying existing payout functionalities are working as expected using KV

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible
  • I added a CHANGELOG entry if applicable

@kashif-m kashif-m force-pushed the payout_kv_interface branch from f9d186b to d5494ad Compare February 29, 2024 08:53
@kashif-m kashif-m self-assigned this Feb 29, 2024
@kashif-m kashif-m linked an issue Feb 29, 2024 that may be closed by this pull request
2 tasks
refactor(payouts): update payouts feature flag dependency and consumption throughout crates
@kashif-m kashif-m force-pushed the payout_kv_interface branch from d4755d3 to 8e81bed Compare February 29, 2024 09:03
@kashif-m kashif-m marked this pull request as ready for review February 29, 2024 09:13
@kashif-m kashif-m requested review from a team as code owners February 29, 2024 09:13
@kashif-m kashif-m force-pushed the payout_kv_interface branch from d6295ea to 8e81bed Compare February 29, 2024 09:14
@sahkal sahkal added the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Mar 1, 2024
@sahkal sahkal added this to the February 2024 Release milestone Mar 1, 2024
@sahkal sahkal added A-routing Area: Routing A-core Area: Core flows labels Mar 1, 2024
crates/diesel_models/src/query/payout_attempt.rs Outdated Show resolved Hide resolved
crates/router/src/core/payment_methods/cards.rs Outdated Show resolved Hide resolved
Comment on lines 18 to 36
pub async fn validate_uniqueness_of_payout_id_against_merchant_id(
db: &dyn StorageInterface,
payout_id: &str,
merchant_id: &str,
storage_scheme: storage::enums::MerchantStorageScheme,
) -> RouterResult<Option<storage::Payouts>> {
let payout = db
.find_payout_by_merchant_id_payout_id(merchant_id, payout_id)
.find_payout_by_merchant_id_payout_id(merchant_id, payout_id, storage_scheme)
.await;
match payout {
Err(err) => {
if err.current_context().is_db_not_found() {
// Empty vec should be returned by query in case of no results, this check exists just
// to be on the safer side. Fixed this, now vector is not returned but should check the flow in detail later.
Ok(None)
} else {
Err(err
let erc = err.current_context();
match erc {
StorageError::ValueNotFound(_) => Ok(None),
_ => Err(err
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while finding payout_attempt, database error"))
.attach_printable("Failed while finding payout_attempt, database error")),
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the correct way of implementing this function, can be confusing for some instance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-looking at this function, it looks good to me.

It checks whether or not the resource exists in DB, and returns an optional result

If Err is returned, it was due to external DB issues.
If Ok(None) is returned, DB call was successful but payout was not found
If Ok(Some) is returned, DB call was successful and payout with given ID already exists.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please have a look at fn generic_find_by_id_optional

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can have an function named something like get_optional_payout_by_merchant_id_payout_id in PayoutInterface which would be calling generic_find_by_id_optional

Comment on lines 97 to 147
+ refund::RefundInterface
+ reverse_lookup::ReverseLookupInterface
+ cards_info::CardsInfoInterface
+ merchant_key_store::MerchantKeyStoreInterface
+ MasterKeyInterface
+ payment_link::PaymentLinkInterface
+ RedisConnInterface
+ RequestIdStore
+ business_profile::BusinessProfileInterface
+ OrganizationInterface
+ routing_algorithm::RoutingAlgorithmInterface
+ gsm::GsmInterface
+ user::UserInterface
+ user_role::UserRoleInterface
+ authorization::AuthorizationInterface
+ user::sample_data::BatchSampleDataInterface
+ health_check::HealthCheckDbInterface
+ role::RoleInterface
+ 'static
{
fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface>;
}

#[cfg(feature = "payouts")]
#[async_trait::async_trait]
pub trait StorageInterface:
Send
+ Sync
+ dyn_clone::DynClone
+ address::AddressInterface
+ api_keys::ApiKeyInterface
+ blocklist_lookup::BlocklistLookupInterface
+ configs::ConfigInterface
+ capture::CaptureInterface
+ customers::CustomerInterface
+ dashboard_metadata::DashboardMetadataInterface
+ dispute::DisputeInterface
+ ephemeral_key::EphemeralKeyInterface
+ events::EventInterface
+ file::FileMetadataInterface
+ FraudCheckInterface
+ locker_mock_up::LockerMockUpInterface
+ mandate::MandateInterface
+ merchant_account::MerchantAccountInterface
+ merchant_connector_account::ConnectorAccessToken
+ merchant_connector_account::MerchantConnectorAccountInterface
+ PaymentAttemptInterface
+ PaymentIntentInterface
+ payment_method::PaymentMethodInterface
+ blocklist::BlocklistInterface
+ blocklist_fingerprint::BlocklistFingerprintInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jarnura and @SanchithHegde can we verify this change, its being duplicated for only payouts interface

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added an empty PayoutsInterface and PayoutAttemptInterface for instances where payouts feature is not enabled. Similar to how it's done for pub trait Connector:

self.diesel_store
.update_payout_by_merchant_id_payout_id(merchant_id, payout_id, payout)
.update_payout(this, payout_update, storage_scheme)
.await
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u pass instrument on every db function

Comment on lines 335 to +352
}

#[cfg(feature = "payouts")]
impl UniqueConstraints for diesel_models::Payouts {
fn unique_constraints(&self) -> Vec<String> {
vec![format!("po_{}_{}", self.merchant_id, self.payout_id)]
}
fn table_name(&self) -> &str {
"Payouts"
}
}

#[cfg(feature = "payouts")]
impl UniqueConstraints for diesel_models::PayoutAttempt {
fn unique_constraints(&self) -> Vec<String> {
vec![format!(
"poa_{}_{}",
self.merchant_id, self.payout_attempt_id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dracarys18 can check this

}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add intruments in such funcs

@kashif-m kashif-m requested a review from sahkal March 5, 2024 06:44
Copy link
Contributor

@sahkal sahkal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ci checks are failing along with merge conflicts can you fix all

@kashif-m kashif-m requested a review from sahkal March 6, 2024 05:51
@SanchithHegde SanchithHegde removed this from the February 2024 Release milestone Mar 6, 2024
@kashif-m kashif-m force-pushed the payout_kv_interface branch from ce878c1 to af6a261 Compare March 18, 2024 06:28
@kashif-m kashif-m requested a review from hrithikesh026 March 18, 2024 06:40
@@ -76,6 +77,19 @@ impl PaymentTokenData {
pub fn wallet_token(payment_method_id: String) -> Self {
Self::WalletToken(WalletTokenData { payment_method_id })
}

pub fn get_token(&self) -> &String {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Chethan-rao - can you review this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of cards, get_token function doesn't really does what is says. Because cards have an object data as opposed to bank_transfer which has only one token field. I feel its better to remove this and generate token for bank transfer by explicitly matching on it

None,
PaymentTokenData::temporary_generic(generate_id(consts::ID_LENGTH, "token")),
),
};

#[cfg(feature = "payouts")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Chethan-rao It's consumed here

hrithikesh026
hrithikesh026 previously approved these changes Mar 18, 2024
@kashif-m kashif-m requested a review from sahkal March 18, 2024 07:44
sahkal
sahkal previously approved these changes Mar 18, 2024
@kashif-m kashif-m dismissed stale reviews from sahkal and hrithikesh026 via 33ecc69 March 18, 2024 08:13
@kashif-m kashif-m force-pushed the payout_kv_interface branch from e4b6d58 to 73d0d29 Compare March 18, 2024 17:36
Copy link
Contributor

@ThisIsMani ThisIsMani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dashboard specific changes looks good.

@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Mar 19, 2024
Merged via the queue into main with commit 944089d Mar 19, 2024
13 of 15 checks passed
@Gnanasundari24 Gnanasundari24 deleted the payout_kv_interface branch March 19, 2024 10:18
@pixincreate pixincreate removed the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Mar 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-core Area: Core flows A-routing Area: Routing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Analytics for payouts
10 participants