mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
feat(payment_methods_v2): Update and Retrieve payment method APIs for v2 (#5939)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -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")]
|
||||
@ -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 {
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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),
|
||||
)
|
||||
@ -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),
|
||||
)
|
||||
@ -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>> =
|
||||
@ -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
|
||||
@ -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)
|
||||
}
|
||||
@ -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(
|
||||
|
||||
@ -1418,26 +1418,26 @@ pub async fn get_fingerprint_id_from_locker<
|
||||
>(
|
||||
state: &routes::SessionState,
|
||||
data: &D,
|
||||
) -> errors::RouterResult<String> {
|
||||
) -> errors::CustomResult<String, errors::VaultError> {
|
||||
let key = data.get_vaulting_data_key();
|
||||
let data = serde_json::to_value(data)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)
|
||||
.attach_printable("Failed to encode Vaulting data to value")?
|
||||
.to_string();
|
||||
|
||||
let payload = pm_types::VaultFingerprintRequest { key, data }
|
||||
.encode_to_vec()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)
|
||||
.attach_printable("Failed to encode VaultFingerprintRequest")?;
|
||||
|
||||
let resp = call_to_vault::<pm_types::GetVaultFingerprint>(state, payload)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::VaultAPIError)
|
||||
.attach_printable("Failed to get response from locker")?;
|
||||
|
||||
let fingerprint_resp: pm_types::VaultFingerprintResponse = resp
|
||||
.parse_struct("VaultFingerprintResp")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::ResponseDeserializationFailed)
|
||||
.attach_printable("Failed to parse data into VaultFingerprintResp")?;
|
||||
|
||||
Ok(fingerprint_resp.fingerprint_id)
|
||||
@ -1448,31 +1448,70 @@ pub async fn get_fingerprint_id_from_locker<
|
||||
pub async fn vault_payment_method_in_locker(
|
||||
state: &routes::SessionState,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
pmd: &api::PaymentMethodCreateData,
|
||||
) -> errors::RouterResult<pm_types::AddVaultResponse> {
|
||||
pmd: &pm_types::PaymentMethodVaultingData,
|
||||
existing_vault_id: Option<String>,
|
||||
) -> errors::CustomResult<pm_types::AddVaultResponse, errors::VaultError> {
|
||||
let payload = pm_types::AddVaultRequest {
|
||||
entity_id: merchant_account.get_id().to_owned(),
|
||||
vault_id: uuid::Uuid::now_v7().to_string(),
|
||||
data: pmd.clone(),
|
||||
vault_id: pm_types::VaultId::generate(
|
||||
existing_vault_id.unwrap_or(uuid::Uuid::now_v7().to_string()),
|
||||
),
|
||||
data: pmd,
|
||||
ttl: state.conf.locker.ttl_for_storage_in_secs,
|
||||
}
|
||||
.encode_to_vec()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)
|
||||
.attach_printable("Failed to encode AddVaultRequest")?;
|
||||
|
||||
let resp = call_to_vault::<pm_types::AddVault>(state, payload)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::VaultAPIError)
|
||||
.attach_printable("Failed to get response from locker")?;
|
||||
|
||||
let stored_pm_resp: pm_types::AddVaultResponse = resp
|
||||
.parse_struct("AddVaultResponse")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.change_context(errors::VaultError::ResponseDeserializationFailed)
|
||||
.attach_printable("Failed to parse data into AddVaultResponse")?;
|
||||
|
||||
Ok(stored_pm_resp)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn retrieve_payment_method_from_vault(
|
||||
state: &routes::SessionState,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
customer_id: &id_type::CustomerId,
|
||||
pm: &domain::PaymentMethod,
|
||||
) -> errors::CustomResult<pm_types::VaultRetrieveResponse, errors::VaultError> {
|
||||
let payload = pm_types::VaultRetrieveRequest {
|
||||
entity_id: merchant_account.get_id().to_owned(),
|
||||
vault_id: pm_types::VaultId::generate(
|
||||
pm.locker_id
|
||||
.clone()
|
||||
.ok_or(errors::VaultError::MissingRequiredField {
|
||||
field_name: "locker_id",
|
||||
})
|
||||
.attach_printable("Missing locker_id for VaultRetrieveRequest")?,
|
||||
),
|
||||
}
|
||||
.encode_to_vec()
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)
|
||||
.attach_printable("Failed to encode VaultRetrieveRequest")?;
|
||||
|
||||
let resp = call_to_vault::<pm_types::VaultRetrieve>(state, payload)
|
||||
.await
|
||||
.change_context(errors::VaultError::VaultAPIError)
|
||||
.attach_printable("Failed to get response from locker")?;
|
||||
|
||||
let stored_pm_resp: pm_types::VaultRetrieveResponse = resp
|
||||
.parse_struct("VaultRetrieveResponse")
|
||||
.change_context(errors::VaultError::ResponseDeserializationFailed)
|
||||
.attach_printable("Failed to parse data into VaultRetrieveResponse")?;
|
||||
|
||||
Ok(stored_pm_resp)
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "payment_methods_v2")
|
||||
@ -1783,18 +1822,6 @@ pub async fn update_customer_payment_method(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn update_customer_payment_method(
|
||||
_state: routes::SessionState,
|
||||
_merchant_account: domain::MerchantAccount,
|
||||
_req: api::PaymentMethodUpdate,
|
||||
_payment_method_id: &str,
|
||||
_key_store: domain::MerchantKeyStore,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "payment_methods_v2")
|
||||
@ -5378,17 +5405,6 @@ pub async fn retrieve_payment_method(
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn retrieve_payment_method(
|
||||
state: routes::SessionState,
|
||||
pm: api::PaymentMethodId,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
) -> errors::RouterResponse<api::PaymentMethodResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v2", feature = "v1"),
|
||||
not(feature = "payment_methods_v2")
|
||||
|
||||
@ -15,6 +15,8 @@ use josekit::jwe;
|
||||
use router_env::tracing_actix_web::RequestId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
use crate::types::payment_methods as pm_types;
|
||||
use crate::{
|
||||
configs::settings,
|
||||
core::errors::{self, CustomResult},
|
||||
@ -520,6 +522,29 @@ pub fn mk_add_card_response_hs(
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
pub fn generate_pm_vaulting_req_from_update_request(
|
||||
pm_create: pm_types::PaymentMethodVaultingData,
|
||||
pm_update: api::PaymentMethodUpdateData,
|
||||
) -> pm_types::PaymentMethodVaultingData {
|
||||
match (pm_create, pm_update) {
|
||||
(
|
||||
pm_types::PaymentMethodVaultingData::Card(card_create),
|
||||
api::PaymentMethodUpdateData::Card(update_card),
|
||||
) => pm_types::PaymentMethodVaultingData::Card(api::CardDetail {
|
||||
card_number: card_create.card_number,
|
||||
card_exp_month: card_create.card_exp_month,
|
||||
card_exp_year: card_create.card_exp_year,
|
||||
card_issuing_country: card_create.card_issuing_country,
|
||||
card_network: card_create.card_network,
|
||||
card_issuer: card_create.card_issuer,
|
||||
card_type: card_create.card_type,
|
||||
card_holder_name: update_card.card_holder_name,
|
||||
nick_name: update_card.nick_name,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
pub fn generate_payment_method_response(
|
||||
pm: &domain::PaymentMethod,
|
||||
@ -798,6 +823,15 @@ pub fn get_card_detail(
|
||||
Ok(card_detail)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
impl From<api::PaymentMethodCreateData> for pm_types::PaymentMethodVaultingData {
|
||||
fn from(item: api::PaymentMethodCreateData) -> Self {
|
||||
match item {
|
||||
api::PaymentMethodCreateData::Card(card) => Self::Card(card),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------TokenizeService------------------------------------------------
|
||||
pub fn mk_crud_locker_request(
|
||||
locker: &settings::Locker,
|
||||
|
||||
@ -2226,7 +2226,8 @@ pub async fn make_pm_data<'a, F: Clone, R, D>(
|
||||
}
|
||||
|
||||
(Some(_), _) => {
|
||||
let (payment_method_data, payment_token) = payment_methods::retrieve_payment_method(
|
||||
let (payment_method_data, payment_token) =
|
||||
payment_methods::retrieve_payment_method_core(
|
||||
&request,
|
||||
state,
|
||||
&payment_data.payment_intent,
|
||||
|
||||
@ -1087,7 +1087,12 @@ impl PaymentMethods {
|
||||
.service(
|
||||
web::resource("/{id}/confirm-intent")
|
||||
.route(web::post().to(confirm_payment_method_intent_api)),
|
||||
);
|
||||
)
|
||||
.service(
|
||||
web::resource("/{id}/update_saved_payment_method")
|
||||
.route(web::patch().to(payment_method_update_api)),
|
||||
)
|
||||
.service(web::resource("/{id}").route(web::get().to(payment_method_retrieve_api)));
|
||||
|
||||
route
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ use super::app::{AppState, SessionState};
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
use crate::core::payment_methods::{
|
||||
create_payment_method, list_customer_payment_method_util, payment_method_intent_confirm,
|
||||
payment_method_intent_create,
|
||||
payment_method_intent_create, retrieve_payment_method, update_payment_method,
|
||||
};
|
||||
use crate::{
|
||||
core::{
|
||||
@ -180,6 +180,65 @@ pub async fn confirm_payment_method_intent_api(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsUpdate))]
|
||||
pub async fn payment_method_update_api(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
json_payload: web::Json<payment_methods::PaymentMethodUpdate>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::PaymentMethodsUpdate;
|
||||
let payment_method_id = path.into_inner();
|
||||
let payload = json_payload.into_inner();
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth, req, _| {
|
||||
update_payment_method(
|
||||
state,
|
||||
auth.merchant_account,
|
||||
req,
|
||||
&payment_method_id,
|
||||
auth.key_store,
|
||||
)
|
||||
},
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsRetrieve))]
|
||||
pub async fn payment_method_retrieve_api(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::PaymentMethodsRetrieve;
|
||||
let payload = web::Json(PaymentMethodId {
|
||||
payment_method_id: path.into_inner(),
|
||||
})
|
||||
.into_inner();
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth, pm, _| {
|
||||
retrieve_payment_method(state, pm, auth.key_store, auth.merchant_account)
|
||||
},
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsMigrate))]
|
||||
pub async fn migrate_payment_method_api(
|
||||
state: web::Data<AppState>,
|
||||
@ -667,6 +726,10 @@ pub async fn render_pm_collect_link(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "payment_methods_v2")
|
||||
))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsRetrieve))]
|
||||
pub async fn payment_method_retrieve_api(
|
||||
state: web::Data<AppState>,
|
||||
@ -693,6 +756,10 @@ pub async fn payment_method_retrieve_api(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(feature = "payment_methods_v2")
|
||||
))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsUpdate))]
|
||||
pub async fn payment_method_update_api(
|
||||
state: web::Data<AppState>,
|
||||
|
||||
@ -8,8 +8,8 @@ pub use api_models::payment_methods::{
|
||||
PaymentMethodIntentConfirm, PaymentMethodIntentConfirmInternal, PaymentMethodIntentCreate,
|
||||
PaymentMethodList, PaymentMethodListData, PaymentMethodListRequest, PaymentMethodListResponse,
|
||||
PaymentMethodMigrate, PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate,
|
||||
PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1,
|
||||
TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
|
||||
PaymentMethodUpdateData, PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest,
|
||||
TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
|
||||
};
|
||||
#[cfg(all(
|
||||
any(feature = "v2", feature = "v1"),
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
use common_utils::generate_id;
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
use masking::Secret;
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
use crate::{
|
||||
@ -19,6 +21,21 @@ pub trait VaultingDataInterface {
|
||||
fn get_vaulting_data_key(&self) -> String;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct VaultId(String);
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
impl VaultId {
|
||||
pub fn get_string_repr(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn generate(id: String) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct VaultFingerprintRequest {
|
||||
@ -36,7 +53,7 @@ pub struct VaultFingerprintResponse {
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct AddVaultRequest<D> {
|
||||
pub entity_id: common_utils::id_type::MerchantId,
|
||||
pub vault_id: String,
|
||||
pub vault_id: VaultId,
|
||||
pub data: D,
|
||||
pub ttl: i64,
|
||||
}
|
||||
@ -45,7 +62,7 @@ pub struct AddVaultRequest<D> {
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct AddVaultResponse {
|
||||
pub entity_id: common_utils::id_type::MerchantId,
|
||||
pub vault_id: String,
|
||||
pub vault_id: VaultId,
|
||||
pub fingerprint_id: Option<String>,
|
||||
}
|
||||
|
||||
@ -57,6 +74,10 @@ pub struct AddVault;
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct GetVaultFingerprint;
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct VaultRetrieve;
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[async_trait::async_trait]
|
||||
impl VaultingInterface for AddVault {
|
||||
@ -75,7 +96,21 @@ impl VaultingInterface for GetVaultFingerprint {
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[async_trait::async_trait]
|
||||
impl VaultingDataInterface for api::PaymentMethodCreateData {
|
||||
impl VaultingInterface for VaultRetrieve {
|
||||
fn get_vaulting_request_url() -> &'static str {
|
||||
consts::VAULT_RETRIEVE_REQUEST_URL
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub enum PaymentMethodVaultingData {
|
||||
Card(api::CardDetail),
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[async_trait::async_trait]
|
||||
impl VaultingDataInterface for PaymentMethodVaultingData {
|
||||
fn get_vaulting_data_key(&self) -> String {
|
||||
match &self {
|
||||
Self::Card(card) => card.card_number.to_string(),
|
||||
@ -101,3 +136,16 @@ pub struct SavedPMLPaymentsInfo {
|
||||
pub off_session_payment_flag: bool,
|
||||
pub is_connector_agnostic_mit_enabled: bool,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct VaultRetrieveRequest {
|
||||
pub entity_id: common_utils::id_type::MerchantId,
|
||||
pub vault_id: VaultId,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct VaultRetrieveResponse {
|
||||
pub data: Secret<String>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user