Skip to content

Commit 48503ff

Browse files
Chethan-raoNishantJoshi00hyperswitch-bot[bot]ShankarSinghC
authored
feat(fingerprint): add api for fingerprint (#76)
Co-authored-by: Nishant Joshi <[email protected]> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Shankar Singh C <[email protected]>
1 parent bf57a3c commit 48503ff

File tree

6 files changed

+127
-5
lines changed

6 files changed

+127
-5
lines changed

docs/openapi/spec.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,27 @@ paths:
156156
oneOf:
157157
- $ref: "#/components/schemas/RetrieveDataRes"
158158
- $ref: "#/components/schemas/JWERes"
159+
/data/fingerprint:
160+
post:
161+
tags:
162+
- Cards
163+
- Data
164+
summary: Get or insert the card fingerprint
165+
description: Get or insert the card fingerprint
166+
requestBody:
167+
description: Provide card number and hash key
168+
content:
169+
application/json:
170+
schema:
171+
$ref: "#/components/schemas/FingerprintReq"
172+
required: true
173+
responses:
174+
"200":
175+
description: Fingerprint Response
176+
content:
177+
application/json:
178+
schema:
179+
$ref: "#/components/schemas/FingerprintRes"
159180
components:
160181
schemas:
161182
Key:
@@ -207,6 +228,14 @@ components:
207228
card_reference:
208229
type: string
209230
example: 3ffdf1e5-7f38-4f26-936f-c66a6f4296fa
231+
FingerprintReq:
232+
type: object
233+
properties:
234+
card:
235+
$ref: "#/components/schemas/FingerprintCardData"
236+
hash_key:
237+
type: string
238+
example: Hash1
210239
JWEReq:
211240
type: object
212241
properties:
@@ -249,6 +278,12 @@ components:
249278
type: string
250279
nick_name:
251280
type: string
281+
FingerprintCardData:
282+
type: object
283+
properties:
284+
card_number:
285+
type: string
286+
example: 4242424242424242
252287
Key1Set:
253288
type: string
254289
description: Response after setting key1
@@ -293,6 +328,18 @@ components:
293328
status:
294329
type: string
295330
enum: [Ok]
331+
FingerprintRes:
332+
type: object
333+
description: Response received if the fingerprint insertion or retrieval was successful
334+
properties:
335+
status:
336+
type: string
337+
enum: [Ok]
338+
payload:
339+
type: object
340+
properties:
341+
fingerprint:
342+
type: string
296343
JWERes:
297344
type: object
298345
description: JWE encrypted response equivalent

src/error/transforms.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ impl<'a> From<&'a super::CryptoError> for super::FingerprintDBError {
113113
}
114114
}
115115

116+
error_transform!(super::FingerprintDBError => super::ApiError);
117+
impl<'a> From<&'a super::FingerprintDBError> for super::ApiError {
118+
fn from(value: &'a super::FingerprintDBError) -> Self {
119+
match value {
120+
super::FingerprintDBError::EncodingError => Self::EncodingError,
121+
super::FingerprintDBError::DBError => Self::DatabaseError,
122+
super::FingerprintDBError::DBFilterError => Self::RetrieveDataFailed("fingerprint"),
123+
super::FingerprintDBError::DBInsertError => Self::DatabaseInsertFailed("fingerprint"),
124+
super::FingerprintDBError::UnknownError => Self::UnknownError,
125+
}
126+
}
127+
}
128+
116129
error_transform!(super::CryptoError => super::HashDBError);
117130
impl<'a> From<&'a super::CryptoError> for super::HashDBError {
118131
fn from(value: &'a super::CryptoError) -> Self {

src/routes/data.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
app::AppState,
1515
crypto::{aes::GcmAes256, sha::Sha512},
1616
error::{self, ContainerError, ResultContainerExt},
17-
storage::{HashInterface, LockerInterface, MerchantInterface},
17+
storage::{FingerprintInterface, HashInterface, LockerInterface, MerchantInterface},
1818
};
1919

2020
#[cfg(feature = "middleware")]
@@ -59,7 +59,8 @@ pub fn serve(
5959
let router = axum::Router::new()
6060
.route("/delete", delete_route)
6161
.route("/add", post(add_card))
62-
.route("/retrieve", post(retrieve_card));
62+
.route("/retrieve", post(retrieve_card))
63+
.route("/fingerprint", post(get_or_insert_fingerprint));
6364

6465
#[cfg(feature = "middleware")]
6566
{
@@ -217,3 +218,18 @@ pub async fn retrieve_card(
217218

218219
Ok(Json(card.try_into()?))
219220
}
221+
222+
/// `/cards/fingerprint` handling the creation and retrieval of card fingerprint
223+
pub async fn get_or_insert_fingerprint(
224+
extract::State(state): extract::State<AppState>,
225+
Json(request): Json<types::FingerprintRequest>,
226+
) -> Result<Json<types::FingerprintResponse>, ContainerError<error::ApiError>> {
227+
request.validate()?;
228+
229+
let fingerprint = state
230+
.db
231+
.insert_fingerprint(request.card.card_number, request.hash_key)
232+
.await?;
233+
234+
Ok(Json(fingerprint.into()))
235+
}

src/routes/data/transformers.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ impl TryFrom<storage::types::Locker> for super::types::RetrieveCardResponse {
7676
}
7777
}
7878

79+
impl From<storage::types::Fingerprint> for super::types::FingerprintResponse {
80+
fn from(value: storage::types::Fingerprint) -> Self {
81+
Self {
82+
card_fingerprint: value.card_fingerprint,
83+
}
84+
}
85+
}
86+
7987
impl std::cmp::PartialEq<types::Data> for super::types::StoredData {
8088
fn eq(&self, other: &types::Data) -> bool {
8189
match (self, other) {

src/routes/data/types.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
// hash2_reference: Option<String>,
88
// }
99

10-
use masking::PeekInterface;
10+
use masking::{PeekInterface, Secret};
1111

12-
use crate::error;
12+
use crate::{error, storage};
1313

1414
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
1515
pub struct Card {
@@ -102,6 +102,22 @@ pub struct DeleteCardResponse {
102102
pub status: Status,
103103
}
104104

105+
#[derive(serde::Deserialize)]
106+
pub struct FingerprintRequest {
107+
pub card: FingerprintCardData,
108+
pub hash_key: Secret<String>,
109+
}
110+
111+
#[derive(serde::Deserialize)]
112+
pub struct FingerprintCardData {
113+
pub card_number: storage::types::CardNumber,
114+
}
115+
116+
#[derive(serde::Serialize, serde::Deserialize)]
117+
pub struct FingerprintResponse {
118+
pub card_fingerprint: Secret<String>,
119+
}
120+
105121
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq)]
106122
pub enum StoredData {
107123
EncData(String),
@@ -132,3 +148,11 @@ impl Validation for StoreCardRequest {
132148
}
133149
}
134150
}
151+
152+
impl Validation for FingerprintRequest {
153+
type Error = error::ApiError;
154+
155+
fn validate(&self) -> Result<(), Self::Error> {
156+
self.card.card_number.validate()
157+
}
158+
}

src/storage/types.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ use diesel::{
66
};
77
use masking::{ExposeInterface, PeekInterface, Secret};
88

9-
use crate::crypto::{self, Encryption};
9+
use crate::{
10+
crypto::{self, Encryption},
11+
error,
12+
routes::data::types::Validation,
13+
};
1014

1115
use super::schema;
1216

@@ -108,6 +112,16 @@ pub struct Fingerprint {
108112
#[derive(Debug, serde::Deserialize)]
109113
pub struct CardNumber(masking::StrongSecret<String>);
110114

115+
impl Validation for CardNumber {
116+
type Error = error::ApiError;
117+
118+
fn validate(&self) -> Result<(), Self::Error> {
119+
crate::validations::luhn_on_string(self.0.peek())
120+
.then_some(())
121+
.ok_or(error::ApiError::ValidationError("card number invalid"))
122+
}
123+
}
124+
111125
impl CardNumber {
112126
pub fn into_bytes(self) -> Vec<u8> {
113127
self.0.peek().clone().into_bytes()

0 commit comments

Comments
 (0)