@@ -3,14 +3,23 @@ mod payments;
33mod ui;
44use std:: {
55 collections:: HashSet ,
6+ fmt,
67 num:: { ParseFloatError , TryFromIntError } ,
8+ str:: FromStr ,
79} ;
810
911pub use accounts:: {
1012 MerchantAccountRequestType , MerchantAccountType , MerchantProductType , OrganizationType ,
1113} ;
14+ use diesel:: {
15+ backend:: Backend ,
16+ deserialize:: FromSql ,
17+ expression:: AsExpression ,
18+ serialize:: { Output , ToSql } ,
19+ sql_types:: Text ,
20+ } ;
1221pub use payments:: ProductType ;
13- use serde:: { Deserialize , Serialize } ;
22+ use serde:: { de , Deserialize , Deserializer , Serialize , Serializer } ;
1423use smithy:: SmithyModel ;
1524pub use ui:: * ;
1625use utoipa:: ToSchema ;
@@ -3081,96 +3090,147 @@ pub enum DisputeStatus {
30813090 DisputeLost ,
30823091}
30833092
3084- #[ derive(
3085- Clone ,
3086- Copy ,
3087- Debug ,
3088- Eq ,
3089- Hash ,
3090- PartialEq ,
3091- serde:: Deserialize ,
3092- serde:: Serialize ,
3093- strum:: Display ,
3094- strum:: EnumString ,
3095- strum:: EnumIter ,
3096- strum:: VariantNames ,
3097- ToSchema ,
3093+ #[ derive( Debug , Clone , AsExpression , PartialEq , ToSchema ) ]
3094+ #[ schema(
3095+ value_type = String ,
3096+ title = "4 digit Merchant category code (MCC)" ,
3097+ example = "5411"
30983098) ]
3099- pub enum MerchantCategory {
3100- #[ serde( rename = "Grocery Stores, Supermarkets (5411)" ) ]
3101- GroceryStoresSupermarkets ,
3102- #[ serde( rename = "Lodging-Hotels, Motels, Resorts-not elsewhere classified (7011)" ) ]
3103- LodgingHotelsMotelsResorts ,
3104- #[ serde( rename = "Agricultural Cooperatives (0763)" ) ]
3105- AgriculturalCooperatives ,
3106- #[ serde( rename = "Attorneys, Legal Services (8111)" ) ]
3107- AttorneysLegalServices ,
3108- #[ serde( rename = "Office and Commercial Furniture (5021)" ) ]
3109- OfficeAndCommercialFurniture ,
3110- #[ serde( rename = "Computer Network/Information Services (4816)" ) ]
3111- ComputerNetworkInformationServices ,
3112- #[ serde( rename = "Shoe Stores (5661)" ) ]
3113- ShoeStores ,
3099+ #[ diesel( sql_type = Text ) ]
3100+ pub struct MerchantCategoryCode ( String ) ;
3101+
3102+ impl MerchantCategoryCode {
3103+ pub fn get_code ( & self ) -> Result < u16 , InvalidMccError > {
3104+ // since self.0 is private field we can safely ensure self.0 is string "0001"-"9999"
3105+ self . 0 . parse :: < u16 > ( ) . map_err ( |_| InvalidMccError {
3106+ message : "Invalid MCC code found" . to_string ( ) ,
3107+ } )
3108+ }
3109+
3110+ pub fn new ( code : u16 ) -> Result < Self , InvalidMccError > {
3111+ if code >= 10000 {
3112+ return Err ( InvalidMccError {
3113+ message : "MCC should be in range 0001 to 9999" . to_string ( ) ,
3114+ } ) ;
3115+ }
3116+ let formatted = format ! ( "{code:04}" ) ;
3117+
3118+ Ok ( Self ( formatted) )
3119+ }
3120+
3121+ pub fn get_category_name ( & self ) -> Result < & str , InvalidMccError > {
3122+ let code = self . get_code ( ) ?;
3123+ match code {
3124+ // specific mapping needs to be depricated
3125+ 5411 => Ok ( "Grocery Stores, Supermarkets (5411)" ) ,
3126+ 7011 => Ok ( "Lodging-Hotels, Motels, Resorts-not elsewhere classified (7011)" ) ,
3127+ 763 => Ok ( "Agricultural Cooperatives (0763)" ) ,
3128+ 8111 => Ok ( "Attorneys, Legal Services (8111)" ) ,
3129+ 5021 => Ok ( "Office and Commercial Furniture (5021)" ) ,
3130+ 4816 => Ok ( "Computer Network/Information Services (4816)" ) ,
3131+ 5661 => Ok ( "Shoe Stores (5661)" ) ,
3132+
3133+ _ => Err ( InvalidMccError {
3134+ message : format ! ( "Category name not found for {}" , code) ,
3135+ } ) ,
3136+ }
3137+ }
3138+
3139+ /// Returns a reference to the 4-digit zero-padded code string.
3140+ pub fn get_string_code ( & self ) -> & str {
3141+ & self . 0
3142+ }
31143143}
31153144
3116- #[ derive(
3117- Clone ,
3118- Copy ,
3119- Debug ,
3120- Eq ,
3121- Hash ,
3122- PartialEq ,
3123- serde:: Deserialize ,
3124- serde:: Serialize ,
3125- strum:: Display ,
3126- strum:: EnumString ,
3127- strum:: EnumIter ,
3128- strum:: VariantNames ,
3129- ToSchema ,
3130- ) ]
3131- #[ router_derive:: diesel_enum( storage_type = "text" ) ]
3132- pub enum MerchantCategoryCode {
3133- #[ serde( rename = "5411" ) ]
3134- #[ strum( serialize = "5411" ) ]
3135- Mcc5411 ,
3136- #[ serde( rename = "7011" ) ]
3137- #[ strum( serialize = "7011" ) ]
3138- Mcc7011 ,
3139- #[ serde( rename = "0763" ) ]
3140- #[ strum( serialize = "0763" ) ]
3141- Mcc0763 ,
3142- #[ serde( rename = "8111" ) ]
3143- #[ strum( serialize = "8111" ) ]
3144- Mcc8111 ,
3145- #[ serde( rename = "5021" ) ]
3146- #[ strum( serialize = "5021" ) ]
3147- Mcc5021 ,
3148- #[ serde( rename = "4816" ) ]
3149- #[ strum( serialize = "4816" ) ]
3150- Mcc4816 ,
3151- #[ serde( rename = "5661" ) ]
3152- #[ strum( serialize = "5661" ) ]
3153- Mcc5661 ,
3145+ impl < DB > ToSql < Text , DB > for MerchantCategoryCode
3146+ where
3147+ DB : Backend ,
3148+ String : ToSql < Text , DB > ,
3149+ {
3150+ fn to_sql < ' b > ( & ' b self , out : & mut Output < ' b , ' _ , DB > ) -> diesel:: serialize:: Result {
3151+ self . 0 . to_sql ( out)
3152+ }
31543153}
3154+ impl < DB : Backend > FromSql < Text , DB > for MerchantCategoryCode
3155+ where
3156+ String : FromSql < Text , DB > ,
3157+ {
3158+ fn from_sql ( bytes : DB :: RawValue < ' _ > ) -> diesel:: deserialize:: Result < Self > {
3159+ let code = <String as FromSql < Text , DB > >:: from_sql ( bytes) ?;
31553160
3156- impl MerchantCategoryCode {
3157- pub fn to_merchant_category_name ( & self ) -> MerchantCategory {
3158- match self {
3159- Self :: Mcc5411 => MerchantCategory :: GroceryStoresSupermarkets ,
3160- Self :: Mcc7011 => MerchantCategory :: LodgingHotelsMotelsResorts ,
3161- Self :: Mcc0763 => MerchantCategory :: AgriculturalCooperatives ,
3162- Self :: Mcc8111 => MerchantCategory :: AttorneysLegalServices ,
3163- Self :: Mcc5021 => MerchantCategory :: OfficeAndCommercialFurniture ,
3164- Self :: Mcc4816 => MerchantCategory :: ComputerNetworkInformationServices ,
3165- Self :: Mcc5661 => MerchantCategory :: ShoeStores ,
3161+ Self :: from_str ( & code) . map_err ( Into :: into)
3162+ }
3163+ }
3164+
3165+ impl Serialize for MerchantCategoryCode {
3166+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
3167+ where
3168+ S : Serializer ,
3169+ {
3170+ serializer. serialize_str ( & self . 0 )
3171+ }
3172+ }
3173+
3174+ #[ derive( Debug , Clone ) ]
3175+ pub struct InvalidMccError {
3176+ pub message : String ,
3177+ }
3178+
3179+ impl fmt:: Display for InvalidMccError {
3180+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3181+ write ! ( f, "Invalid MCC: {}" , self . message)
3182+ }
3183+ }
3184+
3185+ impl std:: error:: Error for InvalidMccError { }
3186+
3187+ impl From < std:: num:: ParseIntError > for InvalidMccError {
3188+ fn from ( err : std:: num:: ParseIntError ) -> Self {
3189+ Self {
3190+ message : format ! ( "MCC must be a number: {}" , err) ,
31663191 }
31673192 }
31683193}
31693194
3195+ impl FromStr for MerchantCategoryCode {
3196+ type Err = InvalidMccError ;
3197+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
3198+ let _code: u16 = s. parse ( ) ?;
3199+
3200+ if s. len ( ) != 4 {
3201+ return Err ( InvalidMccError {
3202+ message : format ! (
3203+ "MCC must be exactly 4 characters long (e.g., '0001'), but got '{}'" ,
3204+ s
3205+ ) ,
3206+ } ) ;
3207+ }
3208+ Ok ( Self ( s. to_string ( ) ) )
3209+ }
3210+ }
3211+
3212+ impl < ' de > Deserialize < ' de > for MerchantCategoryCode {
3213+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
3214+ where
3215+ D : Deserializer < ' de > ,
3216+ {
3217+ let code = String :: deserialize ( deserializer) ?;
3218+
3219+ Self :: from_str ( & code) . map_err ( de:: Error :: custom)
3220+ }
3221+ }
3222+
3223+ impl fmt:: Display for MerchantCategoryCode {
3224+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
3225+ // Format as a zero-padded 4-digit string, which matches the expected enum serialization.
3226+ write ! ( f, "{:04}" , self . 0 )
3227+ }
3228+ }
3229+
31703230#[ derive( serde:: Serialize , serde:: Deserialize , Clone , Debug , PartialEq ) ]
31713231pub struct MerchantCategoryCodeWithName {
31723232 pub code : MerchantCategoryCode ,
3173- pub name : MerchantCategory ,
3233+ pub name : String ,
31743234}
31753235
31763236#[ derive(
0 commit comments