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_methods_v2): Update and Retrieve payment method APIs for v2 #5939

Merged
merged 105 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
b1ffaf6
feat(payment_methods_v2): Payment methods v2 API models
Sarthak1799 Aug 7, 2024
dd73c0c
chore: run formatter
hyperswitch-bot[bot] Aug 7, 2024
8732045
Merge branch 'main' into pm-models-v2
Sarthak1799 Aug 7, 2024
013a793
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Aug 7, 2024
b0cd0f2
fix(payment_methods_v2): script changes
Sarthak1799 Aug 7, 2024
c032d28
Merge branch 'main' of github.com:juspay/hyperswitch into pm-models-v2
Sarthak1799 Aug 8, 2024
8a5ef21
fix(payment_methods_v2): Resolved comments
Sarthak1799 Aug 12, 2024
9ca881c
chore: run formatter
hyperswitch-bot[bot] Aug 12, 2024
78069f4
fix(payment_methods_v2): Resolved comments
Sarthak1799 Aug 12, 2024
39c128c
fix(payment_methods_v2): Resolved comments
Sarthak1799 Aug 13, 2024
2d100a4
Merge branch 'main' into pm-models-v2
Sarthak1799 Aug 14, 2024
169a890
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Aug 14, 2024
bba3b8a
fix(payment_methods_v2): fixed v2 funcs
Sarthak1799 Aug 14, 2024
6b34cbe
Merge branch 'main' of github.com:juspay/hyperswitch into pm-models-v2
Sarthak1799 Aug 14, 2024
5986fbf
Merge branch 'main' into pm-models-v2
Sarthak1799 Aug 20, 2024
2c0e5e6
fix(payment_methods_v2): Fixed errors
Sarthak1799 Aug 21, 2024
194a2ba
fix(payment_methods_v2): Fixed errors
Sarthak1799 Aug 21, 2024
c5b29a2
fix(payment_methods_v2): Resolved comments
Sarthak1799 Aug 22, 2024
aab6e8c
chore: run formatter
hyperswitch-bot[bot] Aug 22, 2024
835bf15
fix(payment_methods_v2): Fixed errors
Sarthak1799 Aug 22, 2024
5009e57
fix(payment_methods_v2): Resolved comments
Sarthak1799 Aug 22, 2024
0dd4f3d
fix(payment_methods_v2): Fixed errors
Sarthak1799 Aug 22, 2024
92e1a68
Merge branch 'main' into pm-models-v2
Sarthak1799 Aug 22, 2024
be4bee6
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Aug 22, 2024
07979b9
Merge branch 'main' of github.com:juspay/hyperswitch into pm-models-v2
Sarthak1799 Aug 22, 2024
813d2aa
Merge branch 'main' of github.com:juspay/hyperswitch into pm-v2-domain
Sarthak1799 Aug 26, 2024
1e832b6
feat(payment_methods_v2): Implemented Diesel and Domain models for v2
Sarthak1799 Aug 26, 2024
22163e8
fix(core): Fixed errors
Sarthak1799 Aug 26, 2024
cb30f48
fix(core): Fixed errors
Sarthak1799 Aug 26, 2024
f55d8c6
fix(core): Fixed migrations
Sarthak1799 Aug 26, 2024
11e5075
fix(core): Fixed errors
Sarthak1799 Aug 27, 2024
8100ef7
fix(core): Resolved comments
Sarthak1799 Aug 27, 2024
de10bb1
Merge branch 'main' of github.com:juspay/hyperswitch into pm-v2-domain
Sarthak1799 Aug 27, 2024
ed10dd4
chore: run formatter
hyperswitch-bot[bot] Aug 27, 2024
6764ed0
fix(core): Fixed errors
Sarthak1799 Aug 27, 2024
b1e48f6
fix(core): Resolved comments
Sarthak1799 Aug 28, 2024
8c0d561
Merge branch 'main' of github.com:juspay/hyperswitch into pm-v2-domain
Sarthak1799 Aug 28, 2024
451c255
fix(core): Fixed errors
Sarthak1799 Aug 28, 2024
cb1be14
diesel: Added fingerprint col + refactor
Sarthak1799 Aug 29, 2024
4d2168e
fix(diesel): Fixed errors for v2
Sarthak1799 Aug 30, 2024
36d3fed
chore: run formatter
hyperswitch-bot[bot] Aug 30, 2024
79a0f41
fix(core): Resolved comments
Sarthak1799 Aug 30, 2024
d1b8dd4
Merge branch 'main' of github.com:juspay/hyperswitch into pm-v2-domain
Sarthak1799 Aug 30, 2024
d7310d2
fix: Fixed errors
Sarthak1799 Aug 30, 2024
f0b24f2
Merge branch 'pm-v2-domain' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Aug 31, 2024
15dc84a
diesel: Added fingerprint lookup query
Sarthak1799 Aug 31, 2024
94ab900
Merge branch 'pm-v2-domain' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Aug 31, 2024
cb9da79
Resolved comments
Sarthak1799 Aug 31, 2024
f4a6e33
fix: Fixed errors
Sarthak1799 Aug 31, 2024
a156223
diesel: Added api_version + resolved comments
Sarthak1799 Sep 2, 2024
dd79289
Merge branch 'main' into pm-v2-domain
Sarthak1799 Sep 2, 2024
028a81b
Merge branch 'pm-v2-domain' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 2, 2024
cca58e8
fix: Resolved comments
Sarthak1799 Sep 2, 2024
2e98fc2
Merge branch 'pm-v2-domain' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 3, 2024
3db7275
Merge branch 'main' of github.com:juspay/hyperswitch into pm-v2-domain
Sarthak1799 Sep 4, 2024
24a7ffc
fix: Fixed error from main
Sarthak1799 Sep 4, 2024
deb48af
Merge branch 'main' of github.com:juspay/hyperswitch into pm-v2-domain
Sarthak1799 Sep 4, 2024
8250f3d
Merge branch 'pm-v2-domain' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 4, 2024
053ebe0
feat(payment_methods_v2): Pm create dev
Sarthak1799 Sep 4, 2024
f204fc1
Merge branch 'main' of github.com:juspay/hyperswitch into pm-create-v2
Sarthak1799 Sep 4, 2024
418704c
chore: run formatter
hyperswitch-bot[bot] Sep 5, 2024
7e95296
fix: Fixed errors
Sarthak1799 Sep 5, 2024
a5fdb25
Merge branch 'pm-create-v2' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 7, 2024
80fa6cd
Merge branch 'main' into pm-create-v2
Sarthak1799 Sep 7, 2024
ad7ef43
fix: Fixed serialzation type for pmd
Sarthak1799 Sep 7, 2024
6bc8abb
Merge branch 'pm-create-v2' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 7, 2024
5c19d5d
fix: Resolved comments
Sarthak1799 Sep 9, 2024
a0f246f
chore: run formatter
hyperswitch-bot[bot] Sep 9, 2024
2c67fdb
fix: Resolved comments
Sarthak1799 Sep 10, 2024
5782d2b
chore: run formatter
hyperswitch-bot[bot] Sep 10, 2024
94f49f1
fix: Resolved comments
Sarthak1799 Sep 10, 2024
adb2320
fix: Resolved comments
Sarthak1799 Sep 11, 2024
8f341c9
chore: run formatter
hyperswitch-bot[bot] Sep 11, 2024
9e646e6
feat: Added Global Id for Payment method
Sarthak1799 Sep 11, 2024
3ad0eb8
Merge branch 'main' of github.com:juspay/hyperswitch into pm-create-v2
Sarthak1799 Sep 11, 2024
bb56bb4
chore: run formatter
hyperswitch-bot[bot] Sep 11, 2024
569a47f
fix
Sarthak1799 Sep 11, 2024
09c074f
fix: Fixed errors
Sarthak1799 Sep 11, 2024
0d3ce8e
fix: Fixed errors
Sarthak1799 Sep 11, 2024
1fcba34
fix: Fixed error type
Sarthak1799 Sep 12, 2024
7be9448
chore: run formatter
hyperswitch-bot[bot] Sep 12, 2024
964e087
fix: Resolved comments
Sarthak1799 Sep 12, 2024
8686a56
Merge branch 'main' of github.com:juspay/hyperswitch into pm-create-v2
Sarthak1799 Sep 12, 2024
24df7ba
chore: run formatter
hyperswitch-bot[bot] Sep 12, 2024
b6d9bce
fix: Fixed errors
Sarthak1799 Sep 13, 2024
abac142
Merge branch 'main' of github.com:juspay/hyperswitch into pm-create-v2
Sarthak1799 Sep 13, 2024
de80fdc
Merge branch 'pm-create-v2' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 14, 2024
0aa583c
Merge branch 'main' of github.com:juspay/hyperswitch into pm-create-v2
Sarthak1799 Sep 16, 2024
4647c03
fix: Fixed errors
Sarthak1799 Sep 16, 2024
a7a4f4b
fix: Fixed errors
Sarthak1799 Sep 16, 2024
e72435c
Merge branch 'main' of github.com:juspay/hyperswitch into pm-create-v2
Sarthak1799 Sep 16, 2024
45c37eb
fix: Fixed errors
Sarthak1799 Sep 16, 2024
ee0986b
Merge branch 'main' into pm-create-v2
Sarthak1799 Sep 17, 2024
375f496
Merge branch 'pm-create-v2' of github.com:juspay/hyperswitch into pm-…
Sarthak1799 Sep 17, 2024
49eded8
feat: Update PM v2
Sarthak1799 Sep 17, 2024
6098148
fix: Fixed errors
Sarthak1799 Sep 18, 2024
0007a71
fix: Added eph auth for retrieve
Sarthak1799 Sep 18, 2024
857ac64
Merge branch 'main' of github.com:juspay/hyperswitch into pm-update-v2
Sarthak1799 Sep 18, 2024
be7cb98
chore: run formatter
hyperswitch-bot[bot] Sep 18, 2024
9fb352b
fix: Fixed typo
Sarthak1799 Sep 18, 2024
610f298
chore: Resolved comments
Sarthak1799 Sep 23, 2024
8bb9228
chore: run formatter
hyperswitch-bot[bot] Sep 23, 2024
ddc3325
fix: Fixed errors
Sarthak1799 Sep 23, 2024
4149b5e
Merge branch 'main' of github.com:juspay/hyperswitch into pm-update-v2
Sarthak1799 Sep 25, 2024
bcc39d2
fix: Fixed errors
Sarthak1799 Sep 26, 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
23 changes: 22 additions & 1 deletion crates/api_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ pub struct PaymentMethodUpdate {
#[serde(deny_unknown_fields)]
pub struct PaymentMethodUpdate {
/// payment method data to be passed
pub payment_method_data: Option<PaymentMethodUpdateData>,
pub payment_method_data: PaymentMethodUpdateData,

/// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK
#[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893eiu2d")]
Expand Down Expand Up @@ -990,6 +990,27 @@ impl From<CardDetailsPaymentMethod> for CardDetailFromLocker {
}
}

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
impl From<CardDetail> for CardDetailFromLocker {
fn from(item: CardDetail) -> Self {
Self {
issuer_country: item.card_issuing_country,
last4_digits: Some(item.card_number.get_last4()),
card_number: Some(item.card_number),
expiry_month: Some(item.card_exp_month),
expiry_year: Some(item.card_exp_year),
card_holder_name: item.card_holder_name,
nick_name: item.nick_name,
card_isin: None,
card_issuer: item.card_issuer,
card_network: item.card_network,
card_type: item.card_type.map(|card| card.to_string()),
saved_to_locker: true,
card_fingerprint: None,
}
}
}

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
impl From<CardDetail> for CardDetailsPaymentMethod {
fn from(item: CardDetail) -> Self {
Expand Down
4 changes: 4 additions & 0 deletions crates/router/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ pub const ADD_VAULT_REQUEST_URL: &str = "/vault/add";
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub const VAULT_FINGERPRINT_REQUEST_URL: &str = "/fingerprint";

/// Vault Retrieve request url
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub const VAULT_RETRIEVE_REQUEST_URL: &str = "/vault/retrieve";

/// Vault Header content type
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub const VAULT_HEADER_CONTENT_TYPE: &str = "application/json";
199 changes: 184 additions & 15 deletions crates/router/src/core/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use common_utils::{consts::DEFAULT_LOCALE, id_type};
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use common_utils::{
crypto::{self, Encryptable},
ext_traits::{AsyncExt, Encode, ValueExt},
ext_traits::{AsyncExt, Encode, StringExt, ValueExt},
fp_utils::when,
generate_id,
request::RequestContent,
Expand Down Expand Up @@ -80,7 +80,7 @@ const PAYMENT_METHOD_STATUS_UPDATE_TASK: &str = "PAYMENT_METHOD_STATUS_UPDATE";
const PAYMENT_METHOD_STATUS_TAG: &str = "PAYMENT_METHOD_STATUS";

#[instrument(skip_all)]
pub async fn retrieve_payment_method(
pub async fn retrieve_payment_method_core(
pm_data: &Option<domain::PaymentMethodData>,
state: &SessionState,
payment_intent: &PaymentIntent,
Expand Down Expand Up @@ -877,16 +877,24 @@ pub async fn create_payment_method(
.await
.attach_printable("Failed to add Payment method to DB")?;

let vaulting_result =
vault_payment_method(state, &req.payment_method_data, merchant_account, key_store).await;
let payment_method_data = pm_types::PaymentMethodVaultingData::from(req.payment_method_data);

let vaulting_result = vault_payment_method(
state,
&payment_method_data,
merchant_account,
key_store,
None,
)
.await;

let response = match vaulting_result {
Ok(resp) => {
let pm_update = create_pm_additional_data_update(
&req.payment_method_data,
&payment_method_data,
state,
key_store,
Some(resp.vault_id),
Some(resp.vault_id.get_string_repr().clone()),
Some(req.payment_method),
Some(req.payment_method_type),
)
Expand Down Expand Up @@ -1041,16 +1049,24 @@ pub async fn payment_method_intent_confirm(
.await
.to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?;

let vaulting_result =
vault_payment_method(state, &req.payment_method_data, merchant_account, key_store).await;
let payment_method_data = pm_types::PaymentMethodVaultingData::from(req.payment_method_data);

let vaulting_result = vault_payment_method(
state,
&payment_method_data,
merchant_account,
key_store,
None,
)
.await;

let response = match vaulting_result {
Ok(resp) => {
let pm_update = create_pm_additional_data_update(
&req.payment_method_data,
&payment_method_data,
state,
key_store,
Some(resp.vault_id),
Some(resp.vault_id.get_string_repr().clone()),
Some(req.payment_method),
Some(req.payment_method_type),
)
Expand Down Expand Up @@ -1220,15 +1236,17 @@ pub async fn create_payment_method_for_intent(

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
pub async fn create_pm_additional_data_update(
pmd: &api::PaymentMethodCreateData,
pmd: &pm_types::PaymentMethodVaultingData,
state: &SessionState,
key_store: &domain::MerchantKeyStore,
vault_id: Option<String>,
payment_method: Option<api_enums::PaymentMethod>,
payment_method_type: Option<api_enums::PaymentMethodType>,
) -> RouterResult<storage::PaymentMethodUpdate> {
let card = match pmd.clone() {
api::PaymentMethodCreateData::Card(card) => api::PaymentMethodsData::Card(card.into()),
let card = match pmd {
pm_types::PaymentMethodVaultingData::Card(card) => {
api::PaymentMethodsData::Card(card.clone().into())
}
};

let pmd: Encryptable<Secret<serde_json::Value>> =
Expand All @@ -1255,15 +1273,17 @@ pub async fn create_pm_additional_data_update(
#[instrument(skip_all)]
pub async fn vault_payment_method(
state: &SessionState,
pmd: &api::PaymentMethodCreateData,
pmd: &pm_types::PaymentMethodVaultingData,
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
existing_vault_id: Option<String>,
) -> RouterResult<pm_types::AddVaultResponse> {
let db = &*state.store;

// get fingerprint_id from locker
let fingerprint_id_from_locker = cards::get_fingerprint_id_from_locker(state, pmd)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to get fingerprint_id from vault")?;

// throw back error if payment method is duplicated
Expand All @@ -1286,7 +1306,10 @@ pub async fn vault_payment_method(
)?;

let resp_from_locker =
cards::vault_payment_method_in_locker(state, merchant_account, pmd).await?;
cards::vault_payment_method_in_locker(state, merchant_account, pmd, existing_vault_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to vault payment method in locker")?;

Ok(resp_from_locker)
}
Expand Down Expand Up @@ -1656,6 +1679,152 @@ async fn generate_saved_pm_response(
Ok(pma)
}

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[instrument(skip_all)]
pub async fn retrieve_payment_method(
state: SessionState,
pm: api::PaymentMethodId,
key_store: domain::MerchantKeyStore,
merchant_account: domain::MerchantAccount,
) -> RouterResponse<api::PaymentMethodResponse> {
let db = state.store.as_ref();
let pm_id = id_type::GlobalPaymentMethodId::generate_from_string(pm.payment_method_id)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to generate GlobalPaymentMethodId")?;

let payment_method = db
.find_payment_method(
&((&state).into()),
&key_store,
&pm_id,
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;

let pmd = payment_method
.payment_method_data
.clone()
.map(|x| x.into_inner().expose())
.and_then(|v| serde_json::from_value::<api::PaymentMethodsData>(v).ok())
.and_then(|pmd| match pmd {
api::PaymentMethodsData::Card(card) => {
Some(api::PaymentMethodResponseData::Card(card.into()))
}
_ => None,
});

let resp = api::PaymentMethodResponse {
merchant_id: payment_method.merchant_id.to_owned(),
customer_id: payment_method.customer_id.to_owned(),
payment_method_id: payment_method.id.get_string_repr().to_string(),
payment_method: payment_method.payment_method,
payment_method_type: payment_method.payment_method_type,
metadata: payment_method.metadata.clone(),
created: Some(payment_method.created_at),
recurring_enabled: false,
last_used_at: Some(payment_method.last_used_at),
client_secret: payment_method.client_secret.clone(),
payment_method_data: pmd,
};

Ok(services::ApplicationResponse::Json(resp))
}

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
#[instrument(skip_all)]
pub async fn update_payment_method(
state: SessionState,
merchant_account: domain::MerchantAccount,
req: api::PaymentMethodUpdate,
payment_method_id: &str,
key_store: domain::MerchantKeyStore,
) -> RouterResponse<api::PaymentMethodResponse> {
let db = state.store.as_ref();

let pm_id = id_type::GlobalPaymentMethodId::generate_from_string(payment_method_id.to_string())
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to generate GlobalPaymentMethodId")?;

let payment_method = db
.find_payment_method(
&((&state).into()),
&key_store,
&pm_id,
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
let current_vault_id = payment_method.locker_id.clone();

when(
payment_method.status == enums::PaymentMethodStatus::AwaitingData,
|| {
Err(errors::ApiErrorResponse::InvalidRequestData {
message: "This Payment method is awaiting data and hence cannot be updated"
.to_string(),
})
},
)?;

let pmd: pm_types::PaymentMethodVaultingData = cards::retrieve_payment_method_from_vault(
&state,
&merchant_account,
&payment_method.customer_id,
&payment_method,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to retrieve payment method from vault")?
.data
.expose()
.parse_struct("PaymentMethodCreateData")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to parse PaymentMethodCreateData")?;

let vault_request_data =
pm_transforms::generate_pm_vaulting_req_from_update_request(pmd, req.payment_method_data);

let vaulting_response = vault_payment_method(
&state,
&vault_request_data,
&merchant_account,
&key_store,
current_vault_id, // using current vault_id for now, will have to refactor this
) // to generate new one on each vaulting later on
.await
.attach_printable("Failed to add payment method in vault")?;

let pm_update = create_pm_additional_data_update(
&vault_request_data,
&state,
&key_store,
Some(vaulting_response.vault_id.get_string_repr().clone()),
payment_method.payment_method,
payment_method.payment_method_type,
)
.await
.attach_printable("Unable to create Payment method data")?;

let payment_method = db
.update_payment_method(
&((&state).into()),
&key_store,
payment_method,
pm_update,
merchant_account.storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update payment method in db")?;

let response = pm_transforms::generate_payment_method_response(&payment_method)?;

// Add a PT task to handle payment_method delete from vault

Ok(services::ApplicationResponse::Json(response))
}

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
impl pm_types::SavedPMLPaymentsInfo {
pub async fn form_payments_info(
Expand Down
Loading
Loading