diff --git a/Cargo.lock b/Cargo.lock index 3e1101fdbf4..5a77720c94f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1858,6 +1858,7 @@ dependencies = [ name = "common_types" version = "0.1.0" dependencies = [ + "cards", "common_enums", "common_utils", "diesel", diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index cf48cef794c..8f7a072881a 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -7459,6 +7459,38 @@ "$ref": "#/components/schemas/ApplePayAddressParameters" } }, + "ApplePayCryptogramData": { + "type": "object", + "description": "This struct represents the cryptogram data for Apple Pay transactions", + "required": [ + "online_payment_cryptogram", + "eci_indicator" + ], + "properties": { + "online_payment_cryptogram": { + "type": "string", + "description": "The online payment cryptogram", + "example": "A1B2C3D4E5F6G7H8" + }, + "eci_indicator": { + "type": "string", + "description": "The ECI (Electronic Commerce Indicator) value", + "example": "05" + } + } + }, + "ApplePayPaymentData": { + "oneOf": [ + { + "$ref": "#/components/schemas/ApplePayPredecryptData" + }, + { + "type": "string", + "description": "This variant contains the encrypted Apple Pay payment data as a string." + } + ], + "description": "This enum is used to represent the Apple Pay payment data, which can either be encrypted or decrypted." + }, "ApplePayPaymentRequest": { "type": "object", "required": [ @@ -7529,6 +7561,36 @@ "recurring" ] }, + "ApplePayPredecryptData": { + "type": "object", + "description": "This struct represents the decrypted Apple Pay payment data", + "required": [ + "application_primary_account_number", + "application_expiration_month", + "application_expiration_year", + "payment_data" + ], + "properties": { + "application_primary_account_number": { + "type": "string", + "description": "The primary account number", + "example": "4242424242424242" + }, + "application_expiration_month": { + "type": "string", + "description": "The application expiration date (PAN expiry month)", + "example": "12" + }, + "application_expiration_year": { + "type": "string", + "description": "The application expiration date (PAN expiry year)", + "example": "24" + }, + "payment_data": { + "$ref": "#/components/schemas/ApplePayCryptogramData" + } + } + }, "ApplePayRecurringDetails": { "type": "object", "required": [ @@ -7705,8 +7767,7 @@ ], "properties": { "payment_data": { - "type": "string", - "description": "The payment data of Apple pay" + "$ref": "#/components/schemas/ApplePayPaymentData" }, "payment_method": { "$ref": "#/components/schemas/ApplepayPaymentMethod" diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index dc3578ef61c..7c07f5c2eb5 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -4467,6 +4467,38 @@ "$ref": "#/components/schemas/ApplePayAddressParameters" } }, + "ApplePayCryptogramData": { + "type": "object", + "description": "This struct represents the cryptogram data for Apple Pay transactions", + "required": [ + "online_payment_cryptogram", + "eci_indicator" + ], + "properties": { + "online_payment_cryptogram": { + "type": "string", + "description": "The online payment cryptogram", + "example": "A1B2C3D4E5F6G7H8" + }, + "eci_indicator": { + "type": "string", + "description": "The ECI (Electronic Commerce Indicator) value", + "example": "05" + } + } + }, + "ApplePayPaymentData": { + "oneOf": [ + { + "$ref": "#/components/schemas/ApplePayPredecryptData" + }, + { + "type": "string", + "description": "This variant contains the encrypted Apple Pay payment data as a string." + } + ], + "description": "This enum is used to represent the Apple Pay payment data, which can either be encrypted or decrypted." + }, "ApplePayPaymentRequest": { "type": "object", "required": [ @@ -4537,6 +4569,36 @@ "recurring" ] }, + "ApplePayPredecryptData": { + "type": "object", + "description": "This struct represents the decrypted Apple Pay payment data", + "required": [ + "application_primary_account_number", + "application_expiration_month", + "application_expiration_year", + "payment_data" + ], + "properties": { + "application_primary_account_number": { + "type": "string", + "description": "The primary account number", + "example": "4242424242424242" + }, + "application_expiration_month": { + "type": "string", + "description": "The application expiration date (PAN expiry month)", + "example": "12" + }, + "application_expiration_year": { + "type": "string", + "description": "The application expiration date (PAN expiry year)", + "example": "24" + }, + "payment_data": { + "$ref": "#/components/schemas/ApplePayCryptogramData" + } + } + }, "ApplePayRecurringDetails": { "type": "object", "required": [ @@ -4713,8 +4775,7 @@ ], "properties": { "payment_data": { - "type": "string", - "description": "The payment data of Apple pay" + "$ref": "#/components/schemas/ApplePayPaymentData" }, "payment_method": { "$ref": "#/components/schemas/ApplepayPaymentMethod" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 8dab325c157..50d1ffd2ee4 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -612,6 +612,7 @@ credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,K [pm_filters.worldpayvantiv] debit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } +apple_pay = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } [pm_filters.zen] boleto = { country = "BR", currency = "BRL" } diff --git a/config/deployments/production.toml b/config/deployments/production.toml index cfbf4d8204d..dbc4cc781ea 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -628,6 +628,7 @@ credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,K [pm_filters.worldpayvantiv] debit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } +apple_pay = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } [pm_filters.zen] boleto = { country = "BR", currency = "BRL" } diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index f4344abb808..507115ba490 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -635,6 +635,7 @@ credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,K [pm_filters.worldpayvantiv] debit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } +apple_pay = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } [pm_filters.zen] boleto = { country = "BR", currency = "BRL" } diff --git a/config/development.toml b/config/development.toml index b3aa9fcb9a5..a7f6a518b3d 100644 --- a/config/development.toml +++ b/config/development.toml @@ -820,6 +820,7 @@ credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,K [pm_filters.worldpayvantiv] debit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } credit = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } +apple_pay = { country = "AF,DZ,AW,AU,AZ,BS,BH,BD,BB,BZ,BM,BT,BO,BA,BW,BR,BN,BG,BI,KH,CA,CV,KY,CL,CO,KM,CD,CR,CZ,DK,DJ,ST,DO,EC,EG,SV,ER,ET,FK,FJ,GM,GE,GH,GI,GT,GN,GY,HT,HN,HK,HU,IS,IN,ID,IR,IQ,IE,IL,IT,JM,JP,JO,KZ,KE,KW,LA,LB,LS,LR,LY,LT,MO,MK,MG,MW,MY,MV,MR,MU,MX,MD,MN,MA,MZ,MM,NA,NZ,NI,NG,KP,NO,AR,PK,PG,PY,PE,UY,PH,PL,GB,QA,OM,RO,RU,RW,WS,SG,ST,ZA,KR,LK,SH,SD,SR,SZ,SE,CH,SY,TW,TJ,TZ,TH,TT,TN,TR,UG,UA,US,UZ,VU,VE,VN,ZM,ZW", currency = "AFN,DZD,ANG,AWG,AUD,AZN,BSD,BHD,BDT,BBD,BZD,BMD,BTN,BOB,BAM,BWP,BRL,BND,BGN,BIF,KHR,CAD,CVE,KYD,XOF,XAF,XPF,CLP,COP,KMF,CDF,CRC,EUR,CZK,DKK,DJF,DOP,XCD,EGP,SVC,ERN,ETB,EUR,FKP,FJD,GMD,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,LAK,LBP,LSL,LRD,LYD,MOP,MKD,MGA,MWK,MYR,MVR,MRU,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,NZD,NIO,NGN,KPW,NOK,ARS,PKR,PAB,PGK,PYG,PEN,UYU,PHP,PLN,GBP,QAR,OMR,RON,RUB,RWF,WST,SAR,RSD,SCR,SLL,SGD,STN,SBD,SOS,ZAR,KRW,LKR,SHP,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TRY,TMT,AED,UGX,UAH,USD,UZS,VUV,VND,YER,CNY,ZMW,ZWL" } [pm_filters.bluecode] bluecode = { country = "AT,BE,BG,HR,CY,CZ,DK,EE,FI,FR,DE,GR,HU,IE,IT,LV,LT,LU,MT,NL,PL,PT,RO,SK,SI,ES,SE,IS,LI,NO", currency = "EUR" } diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 0c238d18c2a..57fb0d9f122 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -3986,7 +3986,8 @@ pub struct GpayTokenizationData { #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct ApplePayWalletData { /// The payment data of Apple pay - pub payment_data: String, + #[schema(value_type = ApplePayPaymentData)] + pub payment_data: common_types::payments::ApplePayPaymentData, /// The payment method of Apple pay pub payment_method: ApplepayPaymentMethod, /// The unique identifier for the transaction diff --git a/crates/common_types/Cargo.toml b/crates/common_types/Cargo.toml index e989426be27..61b3ae128ec 100644 --- a/crates/common_types/Cargo.toml +++ b/crates/common_types/Cargo.toml @@ -22,6 +22,7 @@ error-stack = "0.4.1" common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils"} +cards = { version = "0.1.0", path = "../cards"} euclid = { version = "0.1.0", path = "../euclid" } masking = { version = "0.1.0", path = "../masking" } diff --git a/crates/common_types/src/payments.rs b/crates/common_types/src/payments.rs index 933106f2bf2..d65492953e8 100644 --- a/crates/common_types/src/payments.rs +++ b/crates/common_types/src/payments.rs @@ -3,7 +3,10 @@ use std::collections::HashMap; use common_enums::enums; -use common_utils::{date_time, errors, events, impl_to_sql_from_sql_json, pii, types::MinorUnit}; +use common_utils::{ + date_time, errors, events, ext_traits::OptionExt, impl_to_sql_from_sql_json, pii, + types::MinorUnit, +}; use diesel::{ sql_types::{Jsonb, Text}, AsExpression, FromSqlRow, @@ -13,7 +16,7 @@ use euclid::frontend::{ ast::Program, dir::{DirKeyKind, EuclidDirFilter}, }; -use masking::{PeekInterface, Secret}; +use masking::{ExposeInterface, PeekInterface, Secret}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use utoipa::ToSchema; @@ -409,3 +412,116 @@ pub struct XenditMultipleSplitResponse { pub routes: Vec, } impl_to_sql_from_sql_json!(XenditMultipleSplitResponse); + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +#[serde(untagged)] +/// This enum is used to represent the Apple Pay payment data, which can either be encrypted or decrypted. +pub enum ApplePayPaymentData { + /// This variant contains the decrypted Apple Pay payment data as a structured object. + Decrypted(ApplePayPredecryptData), + /// This variant contains the encrypted Apple Pay payment data as a string. + Encrypted(String), +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +/// This struct represents the decrypted Apple Pay payment data +pub struct ApplePayPredecryptData { + /// The primary account number + #[schema(value_type = String, example = "4242424242424242")] + pub application_primary_account_number: cards::CardNumber, + /// The application expiration date (PAN expiry month) + #[schema(value_type = String, example = "12")] + pub application_expiration_month: Secret, + /// The application expiration date (PAN expiry year) + #[schema(value_type = String, example = "24")] + pub application_expiration_year: Secret, + /// The payment data, which contains the cryptogram and ECI indicator + #[schema(value_type = ApplePayCryptogramData)] + pub payment_data: ApplePayCryptogramData, +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +/// This struct represents the cryptogram data for Apple Pay transactions +pub struct ApplePayCryptogramData { + /// The online payment cryptogram + #[schema(value_type = String, example = "A1B2C3D4E5F6G7H8")] + pub online_payment_cryptogram: Secret, + /// The ECI (Electronic Commerce Indicator) value + #[schema(value_type = String, example = "05")] + pub eci_indicator: Option, +} + +impl ApplePayPaymentData { + /// Get the encrypted Apple Pay payment data if it exists + pub fn get_encrypted_apple_pay_payment_data_optional(&self) -> Option<&String> { + match self { + Self::Encrypted(encrypted_data) => Some(encrypted_data), + Self::Decrypted(_) => None, + } + } + + /// Get the decrypted Apple Pay payment data if it exists + pub fn get_decrypted_apple_pay_payment_data_optional(&self) -> Option<&ApplePayPredecryptData> { + match self { + Self::Encrypted(_) => None, + Self::Decrypted(decrypted_data) => Some(decrypted_data), + } + } + + /// Get the encrypted Apple Pay payment data, returning an error if it does not exist + pub fn get_encrypted_apple_pay_payment_data_mandatory( + &self, + ) -> Result<&String, errors::ValidationError> { + self.get_encrypted_apple_pay_payment_data_optional() + .get_required_value("Encrypted Apple Pay payment data") + .attach_printable("Encrypted Apple Pay payment data is mandatory") + } + + /// Get the decrypted Apple Pay payment data, returning an error if it does not exist + pub fn get_decrypted_apple_pay_payment_data_mandatory( + &self, + ) -> Result<&ApplePayPredecryptData, errors::ValidationError> { + self.get_decrypted_apple_pay_payment_data_optional() + .get_required_value("Decrypted Apple Pay payment data") + .attach_printable("Decrypted Apple Pay payment data is mandatory") + } +} + +impl ApplePayPredecryptData { + /// Get the four-digit expiration year from the Apple Pay pre-decrypt data + pub fn get_two_digit_expiry_year(&self) -> Result, errors::ValidationError> { + let binding = self.application_expiration_year.clone(); + let year = binding.peek(); + Ok(Secret::new( + year.get(year.len() - 2..) + .ok_or(errors::ValidationError::InvalidValue { + message: "Invalid two-digit year".to_string(), + })? + .to_string(), + )) + } + + /// Get the four-digit expiration year from the Apple Pay pre-decrypt data + pub fn get_four_digit_expiry_year(&self) -> Secret { + let mut year = self.application_expiration_year.peek().clone(); + if year.len() == 2 { + year = format!("20{year}"); + } + Secret::new(year) + } + + /// Get the expiration month from the Apple Pay pre-decrypt data + pub fn get_expiry_month(&self) -> Secret { + self.application_expiration_month.clone() + } + + /// Get the expiry date in MMYY format from the Apple Pay pre-decrypt data + pub fn get_expiry_date_as_mmyy(&self) -> Result, errors::ValidationError> { + let year = self.get_two_digit_expiry_year()?.expose(); + let month = self.application_expiration_month.clone().expose(); + Ok(Secret::new(format!("{month}{year}"))) + } +} diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs index 3d04375b106..53ecbf882e6 100644 --- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs @@ -57,10 +57,9 @@ use crate::{ SubmitEvidenceRouterData, }, utils::{ - self, is_manual_capture, missing_field_err, AddressDetailsData, ApplePayDecrypt, - BrowserInformationData, CardData, ForeignTryFrom, - NetworkTokenData as UtilsNetworkTokenData, PaymentsAuthorizeRequestData, PhoneDetailsData, - RouterData as OtherRouterData, + self, is_manual_capture, missing_field_err, AddressDetailsData, BrowserInformationData, + CardData, ForeignTryFrom, NetworkTokenData as UtilsNetworkTokenData, + PaymentsAuthorizeRequestData, PhoneDetailsData, RouterData as OtherRouterData, }, }; @@ -1264,7 +1263,7 @@ pub struct AdyenPazeData { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AdyenApplePayDecryptData { - number: Secret, + number: CardNumber, expiry_month: Secret, expiry_year: Secret, brand: String, @@ -2216,8 +2215,8 @@ impl TryFrom<(&WalletData, &PaymentsAuthorizeRouterData)> for AdyenPaymentMethod if let Some(PaymentMethodToken::ApplePayDecrypt(apple_pay_decrypte)) = item.payment_method_token.clone() { - let expiry_year_4_digit = apple_pay_decrypte.get_four_digit_expiry_year()?; - let exp_month = apple_pay_decrypte.get_expiry_month()?; + let expiry_year_4_digit = apple_pay_decrypte.get_four_digit_expiry_year(); + let exp_month = apple_pay_decrypte.get_expiry_month(); let apple_pay_decrypted_data = AdyenApplePayDecryptData { number: apple_pay_decrypte.application_primary_account_number, expiry_month: exp_month, @@ -2229,8 +2228,14 @@ impl TryFrom<(&WalletData, &PaymentsAuthorizeRouterData)> for AdyenPaymentMethod apple_pay_decrypted_data, ))) } else { + let apple_pay_encrypted_data = data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; let apple_pay_data = AdyenApplePay { - apple_pay_token: Secret::new(data.payment_data.to_string()), + apple_pay_token: Secret::new(apple_pay_encrypted_data.to_string()), }; Ok(AdyenPaymentMethod::ApplePay(Box::new(apple_pay_data))) } diff --git a/crates/hyperswitch_connectors/src/connectors/archipel/transformers.rs b/crates/hyperswitch_connectors/src/connectors/archipel/transformers.rs index 1698c9c4a54..53e7cefc6c8 100644 --- a/crates/hyperswitch_connectors/src/connectors/archipel/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/archipel/transformers.rs @@ -29,8 +29,8 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, unimplemented_payment_method, utils::{ - self, AddressData, AddressDetailsData, ApplePayDecrypt, CardData, CardIssuer, - PaymentsAuthorizeRequestData, RouterData as _, + self, AddressData, AddressDetailsData, CardData, CardIssuer, PaymentsAuthorizeRequestData, + RouterData as _, }, }; @@ -273,8 +273,12 @@ impl TryFrom<(&WalletData, &Option)> for TokenizedCardData { .application_primary_account_number .clone(); - let expiry_year_2_digit = apple_pay_decrypt_data.get_two_digit_expiry_year()?; - let expiry_month = apple_pay_decrypt_data.get_expiry_month()?; + let expiry_year_2_digit = apple_pay_decrypt_data + .get_two_digit_expiry_year() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay expiry year", + })?; + let expiry_month = apple_pay_decrypt_data.get_expiry_month(); Ok(Self { card_data: ArchipelTokenizedCard { @@ -302,7 +306,7 @@ impl TryFrom<(&WalletData, &Option)> for TokenizedCardData { #[derive(Debug, Serialize, Eq, PartialEq, Clone)] #[serde(rename_all = "camelCase")] pub struct ArchipelTokenizedCard { - number: Secret, + number: cards::CardNumber, expiry: CardExpiryDate, scheme: ArchipelCardScheme, } diff --git a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs index 4f8486d1868..ff2b01cdff6 100644 --- a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs @@ -529,6 +529,12 @@ impl TryFrom<&SetupMandateRouterData> for CreateCustomerProfileRequest { } _ => None, }; + let apple_pay_encrypted_data = applepay_token + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; Ok(Self { create_customer_profile_request: AuthorizedotnetZeroMandateRequest { merchant_authentication, @@ -541,9 +547,7 @@ impl TryFrom<&SetupMandateRouterData> for CreateCustomerProfileRequest { customer_type: CustomerType::Individual, payment: PaymentDetails::OpaqueData(WalletDetails { data_descriptor: WalletMethod::Applepay, - data_value: Secret::new( - applepay_token.payment_data.clone(), - ), + data_value: Secret::new(apple_pay_encrypted_data.clone()), }), }, ship_to_list, @@ -2109,10 +2113,18 @@ fn get_wallet_data( data_descriptor: WalletMethod::Googlepay, data_value: Secret::new(wallet_data.get_encoded_wallet_token()?), })), - WalletData::ApplePay(applepay_token) => Ok(PaymentDetails::OpaqueData(WalletDetails { - data_descriptor: WalletMethod::Applepay, - data_value: Secret::new(applepay_token.payment_data.clone()), - })), + WalletData::ApplePay(applepay_token) => { + let apple_pay_encrypted_data = applepay_token + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + Ok(PaymentDetails::OpaqueData(WalletDetails { + data_descriptor: WalletMethod::Applepay, + data_value: Secret::new(apple_pay_encrypted_data.clone()), + })) + } WalletData::PaypalRedirect(_) => Ok(PaymentDetails::PayPal(PayPalDetails { success_url: return_url.to_owned(), cancel_url: return_url.to_owned(), diff --git a/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs b/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs index 5f3bcdf9d37..dccd7b6c6b4 100644 --- a/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs @@ -1,5 +1,6 @@ use base64::Engine; use common_enums::{enums, FutureUsage}; +use common_types::payments::ApplePayPredecryptData; use common_utils::{consts, ext_traits::OptionExt, pii}; use hyperswitch_domain_models::{ payment_method_data::{ @@ -7,8 +8,8 @@ use hyperswitch_domain_models::{ WalletData, }, router_data::{ - AdditionalPaymentMethodConnectorResponse, ApplePayPredecryptData, ConnectorAuthType, - ConnectorResponseData, ErrorResponse, PaymentMethodToken, RouterData, + AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData, + ErrorResponse, PaymentMethodToken, RouterData, }, router_flow_types::refunds::{Execute, RSync}, router_request_types::{ @@ -31,7 +32,7 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, unimplemented_payment_method, utils::{ - self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData, + self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData as OtherRouterData, }, @@ -247,7 +248,7 @@ pub struct Card { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TokenizedCard { - number: Secret, + number: cards::CardNumber, expiration_month: Secret, expiration_year: Secret, cryptogram: Secret, @@ -1045,7 +1046,7 @@ impl TryFrom<&BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>> let client_reference_information = ClientReferenceInformation::from(item); let payment_information = - PaymentInformation::from(&apple_pay_data); + PaymentInformation::try_from(&apple_pay_data)?; let merchant_defined_information = item .router_data .request @@ -2478,7 +2479,7 @@ impl TryFrom<(&SetupMandateRouterData, ApplePayWalletData)> for BankOfAmericaPay "Bank Of America" ))?, }, - None => PaymentInformation::from(&apple_pay_data), + None => PaymentInformation::try_from(&apple_pay_data)?, }; let processing_information = ProcessingInformation::try_from(( Some(PaymentSolution::ApplePay), @@ -2604,8 +2605,8 @@ impl TryFrom<&Box> for PaymentInformation { type Error = error_stack::Report; fn try_from(apple_pay_data: &Box) -> Result { - let expiration_month = apple_pay_data.get_expiry_month()?; - let expiration_year = apple_pay_data.get_four_digit_expiry_year()?; + let expiration_month = apple_pay_data.get_expiry_month(); + let expiration_year = apple_pay_data.get_four_digit_expiry_year(); Ok(Self::ApplePay(Box::new(ApplePayPaymentInformation { tokenized_card: TokenizedCard { @@ -2622,17 +2623,28 @@ impl TryFrom<&Box> for PaymentInformation { } } -impl From<&ApplePayWalletData> for PaymentInformation { - fn from(apple_pay_data: &ApplePayWalletData) -> Self { - Self::ApplePayToken(Box::new(ApplePayTokenPaymentInformation { - fluid_data: FluidData { - value: Secret::from(apple_pay_data.payment_data.clone()), - descriptor: None, - }, - tokenized_card: ApplePayTokenizedCard { - transaction_type: TransactionType::ApplePay, +impl TryFrom<&ApplePayWalletData> for PaymentInformation { + type Error = error_stack::Report; + + fn try_from(apple_pay_data: &ApplePayWalletData) -> Result { + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + + Ok(Self::ApplePayToken(Box::new( + ApplePayTokenPaymentInformation { + fluid_data: FluidData { + value: Secret::from(apple_pay_encrypted_data.clone()), + descriptor: None, + }, + tokenized_card: ApplePayTokenizedCard { + transaction_type: TransactionType::ApplePay, + }, }, - })) + ))) } } diff --git a/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs b/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs index ee735f0b878..563eaef3d57 100644 --- a/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs @@ -31,7 +31,7 @@ use crate::{ }, unimplemented_payment_method, utils::{ - self, ApplePayDecrypt, PaymentsCaptureRequestData, RouterData as OtherRouterData, + self, PaymentsCaptureRequestData, RouterData as OtherRouterData, WalletData as OtherWalletData, }, }; @@ -215,7 +215,7 @@ pub enum PaymentSource { #[derive(Debug, Serialize)] pub struct ApplePayPredecrypt { - token: Secret, + token: cards::CardNumber, #[serde(rename = "type")] decrypt_type: String, token_type: String, @@ -341,8 +341,8 @@ impl TryFrom<&CheckoutRouterData<&PaymentsAuthorizeRouterData>> for PaymentsRequ })) } PaymentMethodToken::ApplePayDecrypt(decrypt_data) => { - let exp_month = decrypt_data.get_expiry_month()?; - let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year()?; + let exp_month = decrypt_data.get_expiry_month(); + let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year(); Ok(PaymentSource::ApplePayPredecrypt(Box::new( ApplePayPredecrypt { token: decrypt_data.application_primary_account_number, diff --git a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs index 27ac86fe7fc..292bd75b6f1 100644 --- a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs @@ -3,6 +3,7 @@ use api_models::payments; use api_models::payouts::PayoutMethodData; use base64::Engine; use common_enums::{enums, FutureUsage}; +use common_types::payments::ApplePayPredecryptData; use common_utils::{ consts, date_time, ext_traits::{OptionExt, ValueExt}, @@ -24,9 +25,8 @@ use hyperswitch_domain_models::{ SamsungPayWalletData, WalletData, }, router_data::{ - AdditionalPaymentMethodConnectorResponse, ApplePayPredecryptData, ConnectorAuthType, - ConnectorResponseData, ErrorResponse, GooglePayDecryptedData, PaymentMethodToken, - RouterData, + AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData, + ErrorResponse, GooglePayDecryptedData, PaymentMethodToken, RouterData, }, router_flow_types::{ payments::Authorize, @@ -61,7 +61,7 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, unimplemented_payment_method, utils::{ - self, AddressDetailsData, ApplePayDecrypt, CardData, CardIssuer, NetworkTokenData as _, + self, AddressDetailsData, CardData, CardIssuer, NetworkTokenData as _, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingRequestData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData as OtherRouterData, @@ -194,8 +194,8 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest { WalletData::ApplePay(apple_pay_data) => match item.payment_method_token.clone() { Some(payment_method_token) => match payment_method_token { PaymentMethodToken::ApplePayDecrypt(decrypt_data) => { - let expiration_month = decrypt_data.get_expiry_month()?; - let expiration_year = decrypt_data.get_four_digit_expiry_year()?; + let expiration_month = decrypt_data.get_expiry_month(); + let expiration_year = decrypt_data.get_four_digit_expiry_year(); ( PaymentInformation::ApplePay(Box::new( ApplePayPaymentInformation { @@ -225,20 +225,28 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest { Err(unimplemented_payment_method!("Google Pay", "Cybersource"))? } }, - None => ( - PaymentInformation::ApplePayToken(Box::new( - ApplePayTokenPaymentInformation { - fluid_data: FluidData { - value: Secret::from(apple_pay_data.payment_data), - descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()), - }, - tokenized_card: ApplePayTokenizedCard { - transaction_type: TransactionType::InApp, + None => { + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + ( + PaymentInformation::ApplePayToken(Box::new( + ApplePayTokenPaymentInformation { + fluid_data: FluidData { + value: Secret::from(apple_pay_encrypted_data.clone()), + descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()), + }, + tokenized_card: ApplePayTokenizedCard { + transaction_type: TransactionType::InApp, + }, }, - }, - )), - Some(PaymentSolution::ApplePay), - ), + )), + Some(PaymentSolution::ApplePay), + ) + } }, WalletData::GooglePay(google_pay_data) => ( PaymentInformation::GooglePayToken(Box::new( @@ -492,7 +500,7 @@ pub struct CardPaymentInformation { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TokenizedCard { - number: Secret, + number: cards::CardNumber, expiration_month: Secret, expiration_year: Secret, cryptogram: Option>, @@ -1811,8 +1819,8 @@ impl Some(apple_pay_wallet_data.payment_method.network.clone()), ))?; let client_reference_information = ClientReferenceInformation::from(item); - let expiration_month = apple_pay_data.get_expiry_month()?; - let expiration_year = apple_pay_data.get_four_digit_expiry_year()?; + let expiration_month = apple_pay_data.get_expiry_month(); + let expiration_year = apple_pay_data.get_four_digit_expiry_year(); let payment_information = PaymentInformation::ApplePay(Box::new(ApplePayPaymentInformation { tokenized_card: TokenizedCard { @@ -1942,12 +1950,7 @@ impl let payment_information = PaymentInformation::GooglePay(Box::new(GooglePayPaymentInformation { tokenized_card: TokenizedCard { - number: Secret::new( - google_pay_decrypted_data - .payment_method_details - .pan - .get_card_no(), - ), + number: google_pay_decrypted_data.payment_method_details.pan, cryptogram: google_pay_decrypted_data.payment_method_details.cryptogram, transaction_type, expiration_year: Secret::new( @@ -2159,10 +2162,21 @@ impl TryFrom<&CybersourceRouterData<&PaymentsAuthorizeRouterData>> for Cybersour ))?; let client_reference_information = ClientReferenceInformation::from(item); + + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context( + errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + }, + )?; let payment_information = PaymentInformation::ApplePayToken( Box::new(ApplePayTokenPaymentInformation { fluid_data: FluidData { - value: Secret::from(apple_pay_data.payment_data), + value: Secret::from( + apple_pay_encrypted_data.clone(), + ), descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()), }, tokenized_card: ApplePayTokenizedCard { diff --git a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs index 5672740c559..f83f8004d15 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use api_models::payments; use cards::CardNumber; use common_enums::{enums, BankNames, CaptureMethod, Currency}; +use common_types::payments::ApplePayPredecryptData; use common_utils::{ crypto::{self, GenerateDigest}, errors::CustomResult, @@ -17,9 +18,7 @@ use hyperswitch_domain_models::{ BankRedirectData, Card, CardDetailsForNetworkTransactionId, GooglePayWalletData, PaymentMethodData, RealTimePaymentData, WalletData, }, - router_data::{ - ApplePayPredecryptData, ConnectorAuthType, ErrorResponse, PaymentMethodToken, RouterData, - }, + router_data::{ConnectorAuthType, ErrorResponse, PaymentMethodToken, RouterData}, router_flow_types::refunds::{Execute, RSync}, router_request_types::{PaymentsAuthorizeData, ResponseId}, router_response_types::{ @@ -47,10 +46,7 @@ use crate::{ PaymentsSyncResponseRouterData, RefundsResponseRouterData, ResponseRouterData, }, unimplemented_payment_method, - utils::{ - self, ApplePayDecrypt, PaymentsAuthorizeRequestData, QrImage, RefundsRequestData, - RouterData as _, - }, + utils::{self, PaymentsAuthorizeRequestData, QrImage, RefundsRequestData, RouterData as _}, }; pub struct FiuuRouterData { @@ -397,7 +393,7 @@ pub struct FiuuApplePayData { txn_channel: TxnChannel, cc_month: Secret, cc_year: Secret, - cc_token: Secret, + cc_token: CardNumber, eci: Option, token_cryptogram: Secret, token_type: FiuuTokenType, @@ -727,8 +723,8 @@ impl TryFrom> for FiuuPaymentMethodData { fn try_from(decrypt_data: Box) -> Result { Ok(Self::FiuuApplePayData(Box::new(FiuuApplePayData { txn_channel: TxnChannel::Creditan, - cc_month: decrypt_data.get_expiry_month()?, - cc_year: decrypt_data.get_four_digit_expiry_year()?, + cc_month: decrypt_data.get_expiry_month(), + cc_year: decrypt_data.get_four_digit_expiry_year(), cc_token: decrypt_data.application_primary_account_number, eci: decrypt_data.payment_data.eci_indicator, token_cryptogram: decrypt_data.payment_data.online_payment_cryptogram, diff --git a/crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs b/crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs index c449ee05122..5390767d6ca 100644 --- a/crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/mollie/transformers.rs @@ -1,6 +1,7 @@ use cards::CardNumber; use common_enums::enums; use common_utils::{pii::Email, request::Method, types::StringMajorUnit}; +use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::{BankDebitData, BankRedirectData, PaymentMethodData, WalletData}, router_data::{ConnectorAuthType, PaymentMethodToken, RouterData}, @@ -329,11 +330,19 @@ fn get_payment_method_for_wallet( shipping_address: get_shipping_details(item)?, }, ))), - WalletData::ApplePay(applepay_wallet_data) => Ok(MolliePaymentMethodData::Applepay( - Box::new(ApplePayMethodData { - apple_pay_payment_token: Secret::new(applepay_wallet_data.payment_data.to_owned()), - }), - )), + WalletData::ApplePay(applepay_wallet_data) => { + let apple_pay_encrypted_data = applepay_wallet_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + Ok(MolliePaymentMethodData::Applepay(Box::new( + ApplePayMethodData { + apple_pay_payment_token: Secret::new(apple_pay_encrypted_data.to_owned()), + }, + ))) + } _ => Err(errors::ConnectorError::NotImplemented("Payment Method".to_string()).into()), } } diff --git a/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs index ec5ce3b2e65..b6eda16611d 100644 --- a/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs @@ -541,7 +541,7 @@ impl TryFrom<(&PaymentMethodData, Option<&PaymentsAuthorizeRouterData>)> for Pay }, PaymentMethodData::Wallet(ref wallet_type) => match wallet_type { WalletData::GooglePay(ref googlepay_data) => Ok(Self::from(googlepay_data)), - WalletData::ApplePay(ref applepay_data) => Ok(Self::from(applepay_data)), + WalletData::ApplePay(ref applepay_data) => Ok(Self::try_from(applepay_data)?), WalletData::AliPayQr(_) | WalletData::AliPayRedirect(_) | WalletData::AliPayHkRedirect(_) @@ -653,12 +653,20 @@ impl From<&GooglePayWalletData> for PaymentMethod { } } -impl From<&ApplePayWalletData> for PaymentMethod { - fn from(wallet_data: &ApplePayWalletData) -> Self { +impl TryFrom<&ApplePayWalletData> for PaymentMethod { + type Error = Error; + fn try_from(apple_pay_wallet_data: &ApplePayWalletData) -> Result { + let apple_pay_encrypted_data = apple_pay_wallet_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + let apple_pay_data = ApplePayData { - applepay_payment_data: Secret::new(wallet_data.payment_data.clone()), + applepay_payment_data: Secret::new(apple_pay_encrypted_data.clone()), }; - Self::ApplePay(Box::new(apple_pay_data)) + Ok(Self::ApplePay(Box::new(apple_pay_data))) } } diff --git a/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs index 010109a06ab..3a5205763d4 100644 --- a/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs @@ -612,21 +612,28 @@ impl TryFrom for NuveiPaymentsRequest { }) } } -impl From for NuveiPaymentsRequest { - fn from(apple_pay_data: ApplePayWalletData) -> Self { - Self { +impl TryFrom for NuveiPaymentsRequest { + type Error = error_stack::Report; + fn try_from(apple_pay_data: ApplePayWalletData) -> Result { + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + Ok(Self { payment_option: PaymentOption { card: Some(Card { external_token: Some(ExternalToken { external_token_provider: ExternalTokenProvider::ApplePay, - mobile_token: Secret::new(apple_pay_data.payment_data), + mobile_token: Secret::new(apple_pay_encrypted_data.clone()), }), ..Default::default() }), ..Default::default() }, ..Default::default() - } + }) } } @@ -917,7 +924,7 @@ where PaymentMethodData::MandatePayment => Self::try_from(item), PaymentMethodData::Wallet(wallet) => match wallet { WalletData::GooglePay(gpay_data) => Self::try_from(gpay_data), - WalletData::ApplePay(apple_pay_data) => Ok(Self::from(apple_pay_data)), + WalletData::ApplePay(apple_pay_data) => Ok(Self::try_from(apple_pay_data)?), WalletData::PaypalRedirect(_) => Self::foreign_try_from(( AlternativePaymentMethodType::Expresscheckout, None, diff --git a/crates/hyperswitch_connectors/src/connectors/payu/transformers.rs b/crates/hyperswitch_connectors/src/connectors/payu/transformers.rs index 45b693ea00e..13014b55482 100644 --- a/crates/hyperswitch_connectors/src/connectors/payu/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/payu/transformers.rs @@ -121,15 +121,25 @@ impl TryFrom<&PayuRouterData<&types::PaymentsAuthorizeRouterData>> for PayuPayme } }), }), - WalletData::ApplePay(data) => Ok(PayuPaymentMethod { - pay_method: PayuPaymentMethodData::Wallet({ - PayuWallet { - value: PayuWalletCode::Jp, - wallet_type: WALLET_IDENTIFIER.to_string(), - authorization_code: Secret::new(data.payment_data), - } - }), - }), + WalletData::ApplePay(apple_pay_data) => { + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + Ok(PayuPaymentMethod { + pay_method: PayuPaymentMethodData::Wallet({ + PayuWallet { + value: PayuWalletCode::Jp, + wallet_type: WALLET_IDENTIFIER.to_string(), + authorization_code: Secret::new( + apple_pay_encrypted_data.to_string(), + ), + } + }), + }) + } _ => Err(errors::ConnectorError::NotImplemented( "Unknown Wallet in Payment Method".to_string(), )), diff --git a/crates/hyperswitch_connectors/src/connectors/rapyd/transformers.rs b/crates/hyperswitch_connectors/src/connectors/rapyd/transformers.rs index fb77ffe541c..496dac42f48 100644 --- a/crates/hyperswitch_connectors/src/connectors/rapyd/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/rapyd/transformers.rs @@ -147,10 +147,18 @@ impl TryFrom<&RapydRouterData<&types::PaymentsAuthorizeRouterData>> for RapydPay payment_type: "google_pay".to_string(), token: Some(Secret::new(data.tokenization_data.token.to_owned())), }), - WalletData::ApplePay(data) => Some(RapydWallet { - payment_type: "apple_pay".to_string(), - token: Some(Secret::new(data.payment_data.to_string())), - }), + WalletData::ApplePay(data) => { + let apple_pay_encrypted_data = data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + Some(RapydWallet { + payment_type: "apple_pay".to_string(), + token: Some(Secret::new(apple_pay_encrypted_data.to_string())), + }) + } _ => None, }; Some(PaymentMethod { diff --git a/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs b/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs index 3cd7aa033d7..164f8857afb 100644 --- a/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs @@ -45,7 +45,7 @@ use url::Url; use crate::{ constants::headers::STRIPE_COMPATIBLE_CONNECT_ACCOUNT, - utils::{convert_uppercase, ApplePay, ApplePayDecrypt, RouterData as OtherRouterData}, + utils::{convert_uppercase, ApplePay, RouterData as OtherRouterData}, }; #[cfg(feature = "payouts")] pub mod connect; @@ -590,7 +590,7 @@ pub enum StripeWallet { #[derive(Debug, Eq, PartialEq, Serialize)] pub struct StripeApplePayPredecrypt { #[serde(rename = "card[number]")] - number: Secret, + number: cards::CardNumber, #[serde(rename = "card[exp_year]")] exp_year: Secret, #[serde(rename = "card[exp_month]")] @@ -1481,14 +1481,12 @@ impl TryFrom<(&WalletData, Option)> for StripePaymentMethodD if let Some(PaymentMethodToken::ApplePayDecrypt(decrypt_data)) = payment_method_token { - let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year()?; - let exp_month = decrypt_data.get_expiry_month()?; - + let expiry_year_4_digit = decrypt_data.get_four_digit_expiry_year(); Some(Self::Wallet(StripeWallet::ApplePayPredecryptToken( Box::new(StripeApplePayPredecrypt { number: decrypt_data.clone().application_primary_account_number, exp_year: expiry_year_4_digit, - exp_month, + exp_month: decrypt_data.application_expiration_month, eci: decrypt_data.payment_data.eci_indicator, cryptogram: decrypt_data.payment_data.online_payment_cryptogram, tokenization_method: "apple_pay".to_string(), diff --git a/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs b/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs index 413551a9fdf..8e79d668dd2 100644 --- a/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs @@ -1,17 +1,19 @@ use api_models::payments; use base64::Engine; use common_enums::{enums, FutureUsage}; +use common_types::payments::ApplePayPredecryptData; use common_utils::{ consts, pii, types::{SemanticVersion, StringMajorUnit}, }; +use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::{ ApplePayWalletData, BankDebitData, GooglePayWalletData, PaymentMethodData, WalletData, }, router_data::{ - AdditionalPaymentMethodConnectorResponse, ApplePayPredecryptData, ConnectorAuthType, - ConnectorResponseData, ErrorResponse, PaymentMethodToken, RouterData, + AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData, + ErrorResponse, PaymentMethodToken, RouterData, }, router_flow_types::{ payments::Authorize, @@ -38,7 +40,7 @@ use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, unimplemented_payment_method, utils::{ - self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData, + self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData as OtherRouterData, }, @@ -126,8 +128,8 @@ impl TryFrom<&SetupMandateRouterData> for WellsfargoZeroMandateRequest { WalletData::ApplePay(apple_pay_data) => match item.payment_method_token.clone() { Some(payment_method_token) => match payment_method_token { PaymentMethodToken::ApplePayDecrypt(decrypt_data) => { - let expiration_month = decrypt_data.get_expiry_month()?; - let expiration_year = decrypt_data.get_four_digit_expiry_year()?; + let expiration_month = decrypt_data.get_expiry_month(); + let expiration_year = decrypt_data.get_four_digit_expiry_year(); ( PaymentInformation::ApplePay(Box::new( ApplePayPaymentInformation { @@ -157,20 +159,28 @@ impl TryFrom<&SetupMandateRouterData> for WellsfargoZeroMandateRequest { Err(unimplemented_payment_method!("Google Pay", "Wellsfargo"))? } }, - None => ( - PaymentInformation::ApplePayToken(Box::new( - ApplePayTokenPaymentInformation { - fluid_data: FluidData { - value: Secret::from(apple_pay_data.payment_data), - descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()), - }, - tokenized_card: ApplePayTokenizedCard { - transaction_type: TransactionType::ApplePay, + None => { + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; + ( + PaymentInformation::ApplePayToken(Box::new( + ApplePayTokenPaymentInformation { + fluid_data: FluidData { + value: Secret::from(apple_pay_encrypted_data.clone()), + descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()), + }, + tokenized_card: ApplePayTokenizedCard { + transaction_type: TransactionType::ApplePay, + }, }, - }, - )), - Some(PaymentSolution::ApplePay), - ), + )), + Some(PaymentSolution::ApplePay), + ) + } }, WalletData::GooglePay(google_pay_data) => ( PaymentInformation::GooglePay(Box::new(GooglePayPaymentInformation { @@ -371,7 +381,7 @@ pub struct CardPaymentInformation { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TokenizedCard { - number: Secret, + number: cards::CardNumber, expiration_month: Secret, expiration_year: Secret, cryptogram: Secret, @@ -991,8 +1001,8 @@ impl Some(apple_pay_wallet_data.payment_method.network.clone()), ))?; let client_reference_information = ClientReferenceInformation::from(item); - let expiration_month = apple_pay_data.get_expiry_month()?; - let expiration_year = apple_pay_data.get_four_digit_expiry_year()?; + let expiration_month = apple_pay_data.get_expiry_month(); + let expiration_year = apple_pay_data.get_four_digit_expiry_year(); let payment_information = PaymentInformation::ApplePay(Box::new(ApplePayPaymentInformation { tokenized_card: TokenizedCard { @@ -1201,10 +1211,21 @@ impl TryFrom<&WellsfargoRouterData<&PaymentsAuthorizeRouterData>> for Wellsfargo ))?; let client_reference_information = ClientReferenceInformation::from(item); + + let apple_pay_encrypted_data = apple_pay_data + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context( + errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + }, + )?; let payment_information = PaymentInformation::ApplePayToken( Box::new(ApplePayTokenPaymentInformation { fluid_data: FluidData { - value: Secret::from(apple_pay_data.payment_data), + value: Secret::from( + apple_pay_encrypted_data.to_string(), + ), descriptor: Some(FLUID_DATA_DESCRIPTOR.to_string()), }, tokenized_card: ApplePayTokenizedCard { diff --git a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs index 01736d1f474..a6b7bcd0fa3 100644 --- a/crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs +++ b/crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs @@ -786,6 +786,18 @@ static WORLDPAYVANTIV_SUPPORTED_PAYMENT_METHODS: LazyLock, #[serde(skip_serializing_if = "Option::is_none")] pub original_network_transaction_id: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub cardholder_authentication: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CardholderAuthentication { + authentication_value: Secret, } #[derive(Debug, Serialize)] @@ -249,6 +257,7 @@ pub struct RefundRequest { #[serde(rename_all = "lowercase")] pub enum OrderSource { Ecommerce, + ApplePay, MailOrder, Telephone, } @@ -297,6 +306,31 @@ pub enum WorldpayvativCardType { UnionPay, } +#[derive(Debug, Clone, Serialize, strum::EnumString)] +pub enum WorldPayVativApplePayNetwork { + Visa, + MasterCard, + AmEx, + Discover, + DinersClub, + JCB, + UnionPay, +} + +impl From for WorldpayvativCardType { + fn from(network: WorldPayVativApplePayNetwork) -> Self { + match network { + WorldPayVativApplePayNetwork::Visa => Self::Visa, + WorldPayVativApplePayNetwork::MasterCard => Self::MasterCard, + WorldPayVativApplePayNetwork::AmEx => Self::AmericanExpress, + WorldPayVativApplePayNetwork::Discover => Self::Discover, + WorldPayVativApplePayNetwork::DinersClub => Self::DinersClub, + WorldPayVativApplePayNetwork::JCB => Self::JCB, + WorldPayVativApplePayNetwork::UnionPay => Self::UnionPay, + } + } +} + impl TryFrom for WorldpayvativCardType { type Error = error_stack::Report; fn try_from(card_network: common_enums::CardNetwork) -> Result { @@ -496,7 +530,7 @@ impl TryFrom<&WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>> for CnpOnl })? }; - let card = get_vantiv_card_data(&item.router_data.request.payment_method_data.clone())?; + let (card, cardholder_authentication) = get_vantiv_card_data(item)?; let report_group = item .router_data .request @@ -524,7 +558,7 @@ impl TryFrom<&WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>> for CnpOnl let bill_to_address = get_bill_to_address(item.router_data); let ship_to_address = get_ship_to_address(item.router_data); let processing_info = get_processing_info(&item.router_data.request)?; - let order_source = OrderSource::from(&item.router_data.request.payment_channel); + let order_source = OrderSource::from(item); let (authorization, sale) = if item.router_data.request.is_auto_capture()? && item.amount != MinorUnit::zero() { ( @@ -571,6 +605,7 @@ impl TryFrom<&WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>> for CnpOnl token: processing_info.token, processing_type: processing_info.processing_type, original_network_transaction_id: processing_info.network_transaction_id, + cardholder_authentication, }), None, ) @@ -590,9 +625,16 @@ impl TryFrom<&WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>> for CnpOnl } } -impl From<&Option> for OrderSource { - fn from(payment_channel: &Option) -> Self { - match payment_channel { +impl From<&WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>> for OrderSource { + fn from(item: &WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>) -> Self { + if let PaymentMethodData::Wallet( + hyperswitch_domain_models::payment_method_data::WalletData::ApplePay(_), + ) = &item.router_data.request.payment_method_data + { + return Self::ApplePay; + } + + match item.router_data.request.payment_channel { Some(common_enums::PaymentChannel::Ecommerce) | Some(common_enums::PaymentChannel::Other(_)) | None => Self::Ecommerce, @@ -2998,8 +3040,15 @@ fn get_refund_status( } fn get_vantiv_card_data( - payment_method_data: &PaymentMethodData, -) -> Result, error_stack::Report> { + item: &WorldpayvantivRouterData<&PaymentsAuthorizeRouterData>, +) -> Result< + ( + Option, + Option, + ), + error_stack::Report, +> { + let payment_method_data = item.router_data.request.payment_method_data.clone(); match payment_method_data { PaymentMethodData::Card(card) => { let card_type = match card.card_network.clone() { @@ -3009,12 +3058,15 @@ fn get_vantiv_card_data( let exp_date = card.get_expiry_date_as_mmyy()?; - Ok(Some(WorldpayvantivCardData { - card_type, - number: card.card_number.clone(), - exp_date, - card_validation_num: Some(card.card_cvc.clone()), - })) + Ok(( + Some(WorldpayvantivCardData { + card_type, + number: card.card_number.clone(), + exp_date, + card_validation_num: Some(card.card_cvc.clone()), + }), + None, + )) } PaymentMethodData::CardDetailsForNetworkTransactionId(card_data) => { let card_type = match card_data.card_network.clone() { @@ -3024,14 +3076,73 @@ fn get_vantiv_card_data( let exp_date = card_data.get_expiry_date_as_mmyy()?; - Ok(Some(WorldpayvantivCardData { - card_type, - number: card_data.card_number.clone(), - exp_date, - card_validation_num: None, - })) + Ok(( + Some(WorldpayvantivCardData { + card_type, + number: card_data.card_number.clone(), + exp_date, + card_validation_num: None, + }), + None, + )) } - PaymentMethodData::MandatePayment => Ok(None), + PaymentMethodData::MandatePayment => Ok((None, None)), + PaymentMethodData::Wallet(wallet_data) => match wallet_data { + hyperswitch_domain_models::payment_method_data::WalletData::ApplePay( + apple_pay_data, + ) => match item.router_data.payment_method_token.clone() { + Some( + hyperswitch_domain_models::router_data::PaymentMethodToken::ApplePayDecrypt( + apple_pay_decrypted_data, + ), + ) => { + let number = apple_pay_decrypted_data + .application_primary_account_number + .clone(); + let exp_date = apple_pay_decrypted_data + .get_expiry_date_as_mmyy() + .change_context(errors::ConnectorError::InvalidDataFormat { + field_name: "payment_method_data.card.card_exp_month", + })?; + + let cardholder_authentication = CardholderAuthentication { + authentication_value: apple_pay_decrypted_data + .payment_data + .online_payment_cryptogram + .clone(), + }; + + let apple_pay_network = apple_pay_data + .payment_method + .network + .parse::() + .change_context(errors::ConnectorError::ParsingFailed) + .attach_printable_lazy(|| { + format!( + "Failed to parse Apple Pay network: {}", + apple_pay_data.payment_method.network + ) + })?; + + Ok(( + (Some(WorldpayvantivCardData { + card_type: apple_pay_network.into(), + number, + exp_date, + card_validation_num: None, + })), + Some(cardholder_authentication), + )) + } + _ => Err( + errors::ConnectorError::NotImplemented("Payment method type".to_string()) + .into(), + ), + }, + _ => Err( + errors::ConnectorError::NotImplemented("Payment method type".to_string()).into(), + ), + }, _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()), } } diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index d2ee231d144..7222acddc37 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -50,7 +50,7 @@ use hyperswitch_domain_models::{ network_tokenization::NetworkTokenNumber, payment_method_data::{self, Card, CardDetailsForNetworkTransactionId, PaymentMethodData}, router_data::{ - ApplePayPredecryptData, ErrorResponse, PaymentMethodToken, RecurringMandatePaymentData, + ErrorResponse, PaymentMethodToken, RecurringMandatePaymentData, RouterData as ConnectorRouterData, }, router_request_types::{ @@ -1024,40 +1024,6 @@ impl AccessTokenRequestInfo for RefreshTokenRouterData { .ok_or_else(missing_field_err("request.id")) } } -pub trait ApplePayDecrypt { - fn get_expiry_month(&self) -> Result, Error>; - fn get_two_digit_expiry_year(&self) -> Result, Error>; - fn get_four_digit_expiry_year(&self) -> Result, Error>; -} - -impl ApplePayDecrypt for Box { - fn get_two_digit_expiry_year(&self) -> Result, Error> { - Ok(Secret::new( - self.application_expiration_date - .get(0..2) - .ok_or(errors::ConnectorError::RequestEncodingFailed)? - .to_string(), - )) - } - - fn get_four_digit_expiry_year(&self) -> Result, Error> { - Ok(Secret::new(format!( - "20{}", - self.application_expiration_date - .get(0..2) - .ok_or(errors::ConnectorError::RequestEncodingFailed)? - ))) - } - - fn get_expiry_month(&self) -> Result, Error> { - Ok(Secret::new( - self.application_expiration_date - .get(2..4) - .ok_or(errors::ConnectorError::RequestEncodingFailed)? - .to_owned(), - )) - } -} #[derive(Debug, Copy, Clone, strum::Display, Eq, Hash, PartialEq)] pub enum CardIssuer { @@ -5791,12 +5757,20 @@ pub trait ApplePay { impl ApplePay for payment_method_data::ApplePayWalletData { fn get_applepay_decoded_payment_data(&self) -> Result, Error> { + let apple_pay_encrypted_data = self + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; let token = Secret::new( - String::from_utf8(BASE64_ENGINE.decode(&self.payment_data).change_context( - errors::ConnectorError::InvalidWalletToken { - wallet_name: "Apple Pay".to_string(), - }, - )?) + String::from_utf8( + BASE64_ENGINE + .decode(apple_pay_encrypted_data) + .change_context(errors::ConnectorError::InvalidWalletToken { + wallet_name: "Apple Pay".to_string(), + })?, + ) .change_context(errors::ConnectorError::InvalidWalletToken { wallet_name: "Apple Pay".to_string(), })?, diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index adb4e380782..d4e89a1cf6f 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -474,7 +474,7 @@ pub struct GpayTokenizationData { #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct ApplePayWalletData { /// The payment data of Apple pay - pub payment_data: String, + pub payment_data: common_types::payments::ApplePayPaymentData, /// The payment method of Apple pay pub payment_method: ApplepayPaymentMethod, /// The unique identifier for the transaction diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index 8cdcfe7e456..c77f0e3a7bb 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, marker::PhantomData}; -use common_types::primitive_wrappers; +use common_types::{payments as common_payment_types, primitive_wrappers}; use common_utils::{ errors::IntegrityCheckError, ext_traits::{OptionExt, ValueExt}, @@ -244,30 +244,81 @@ pub struct AccessToken { #[derive(Debug, Clone, serde::Deserialize)] pub enum PaymentMethodToken { Token(Secret), - ApplePayDecrypt(Box), + ApplePayDecrypt(Box), GooglePayDecrypt(Box), PazeDecrypt(Box), } #[derive(Debug, Clone, serde::Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ApplePayPredecryptData { - pub application_primary_account_number: Secret, +pub struct ApplePayPredecryptDataInternal { + pub application_primary_account_number: cards::CardNumber, pub application_expiration_date: String, pub currency_code: String, pub transaction_amount: i64, pub device_manufacturer_identifier: Secret, pub payment_data_type: Secret, - pub payment_data: ApplePayCryptogramData, + pub payment_data: ApplePayCryptogramDataInternal, } #[derive(Debug, Clone, serde::Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ApplePayCryptogramData { +pub struct ApplePayCryptogramDataInternal { pub online_payment_cryptogram: Secret, pub eci_indicator: Option, } +impl TryFrom for common_payment_types::ApplePayPredecryptData { + type Error = common_utils::errors::ValidationError; + fn try_from(data: ApplePayPredecryptDataInternal) -> Result { + let application_expiration_month = data.clone().get_expiry_month()?; + let application_expiration_year = data.clone().get_four_digit_expiry_year()?; + + Ok(Self { + application_primary_account_number: data.application_primary_account_number.clone(), + application_expiration_month, + application_expiration_year, + payment_data: data.payment_data.into(), + }) + } +} + +impl From for common_payment_types::ApplePayCryptogramData { + fn from(payment_data: ApplePayCryptogramDataInternal) -> Self { + Self { + online_payment_cryptogram: payment_data.online_payment_cryptogram, + eci_indicator: payment_data.eci_indicator, + } + } +} + +impl ApplePayPredecryptDataInternal { + /// This logic being applied as apple pay provides application_expiration_date in the YYMMDD format + fn get_four_digit_expiry_year( + &self, + ) -> Result, common_utils::errors::ValidationError> { + Ok(Secret::new(format!( + "20{}", + self.application_expiration_date.get(0..2).ok_or( + common_utils::errors::ValidationError::InvalidValue { + message: "Invalid two-digit year".to_string(), + } + )? + ))) + } + + fn get_expiry_month(&self) -> Result, common_utils::errors::ValidationError> { + Ok(Secret::new( + self.application_expiration_date + .get(2..4) + .ok_or(common_utils::errors::ValidationError::InvalidValue { + message: "Invalid two-digit month".to_string(), + })? + .to_owned(), + )) + } +} + #[derive(Debug, Clone, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub struct GooglePayDecryptedData { diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 58e178f5b9a..344036a198f 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -237,6 +237,9 @@ Never share your secret api keys. Keep them guarded and secure. common_utils::payout_method_utils::PaypalAdditionalData, common_utils::payout_method_utils::VenmoAdditionalData, common_types::payments::SplitPaymentsRequest, + common_types::payments::ApplePayPaymentData, + common_types::payments::ApplePayPredecryptData, + common_types::payments::ApplePayCryptogramData, common_types::payments::StripeSplitPaymentRequest, common_types::domain::AdyenSplitData, common_types::domain::AdyenSplitItem, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 23f0fa2d0d2..a8e44940a0c 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -179,6 +179,10 @@ Never share your secret api keys. Keep them guarded and secure. common_utils::payout_method_utils::PaypalAdditionalData, common_utils::payout_method_utils::VenmoAdditionalData, common_types::payments::SplitPaymentsRequest, + common_types::payments::ApplePayPaymentData, + common_types::payments::ApplePayPredecryptData, + common_types::payments::ApplePayCryptogramData, + common_types::payments::ApplePayPaymentData, common_types::payments::StripeSplitPaymentRequest, common_types::domain::AdyenSplitData, common_types::payments::AcceptanceType, diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 5deeeadb41f..fb6ee799026 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -40,7 +40,7 @@ use crate::{ self, api, domain, storage::enums as storage_enums, transformers::{ForeignFrom, ForeignTryFrom}, - ApplePayPredecryptData, BrowserInformation, PaymentsCancelData, ResponseId, + BrowserInformation, PaymentsCancelData, ResponseId, }, utils::{OptionExt, ValueExt}, }; @@ -1686,10 +1686,16 @@ pub trait ApplePay { impl ApplePay for domain::ApplePayWalletData { fn get_applepay_decoded_payment_data(&self) -> Result, Error> { + let apple_pay_encrypted_data = self + .payment_data + .get_encrypted_apple_pay_payment_data_mandatory() + .change_context(errors::ConnectorError::MissingRequiredField { + field_name: "Apple pay encrypted data", + })?; let token = Secret::new( String::from_utf8( consts::BASE64_ENGINE - .decode(&self.payment_data) + .decode(apple_pay_encrypted_data) .change_context(errors::ConnectorError::InvalidWalletToken { wallet_name: "Apple Pay".to_string(), })?, @@ -1702,31 +1708,6 @@ impl ApplePay for domain::ApplePayWalletData { } } -pub trait ApplePayDecrypt { - fn get_expiry_month(&self) -> Result, Error>; - fn get_four_digit_expiry_year(&self) -> Result, Error>; -} - -impl ApplePayDecrypt for Box { - fn get_four_digit_expiry_year(&self) -> Result, Error> { - Ok(Secret::new(format!( - "20{}", - self.application_expiration_date - .get(0..2) - .ok_or(errors::ConnectorError::RequestEncodingFailed)? - ))) - } - - fn get_expiry_month(&self) -> Result, Error> { - Ok(Secret::new( - self.application_expiration_date - .get(2..4) - .ok_or(errors::ConnectorError::RequestEncodingFailed)? - .to_owned(), - )) - } -} - pub trait CryptoData { fn get_pay_currency(&self) -> Result; } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index a912dc27b82..3c2a447120f 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3562,6 +3562,13 @@ where _ => return Ok(None), }; + // Check if the wallet has already decrypted the token from the payment data. + // If a pre-decrypted token is available, use it directly to avoid redundant decryption. + if let Some(predecrypted_token) = wallet.check_predecrypted_token(payment_data)? { + logger::debug!("Using predecrypted token for wallet"); + return Ok(Some(predecrypted_token)); + } + let merchant_connector_account = get_merchant_connector_account_for_wallet_decryption_flow::( state, @@ -4902,6 +4909,15 @@ where F: Send + Clone, D: OperationSessionGetters + Send + Sync + Clone, { + /// Check if wallet data is already decrypted and return token if so + fn check_predecrypted_token( + &self, + _payment_data: &D, + ) -> CustomResult, errors::ApiErrorResponse> { + // Default implementation returns None (no pre-decrypted data) + Ok(None) + } + fn decide_wallet_flow( &self, state: &SessionState, @@ -4990,6 +5006,33 @@ where F: Send + Clone, D: OperationSessionGetters + Send + Sync + Clone, { + fn check_predecrypted_token( + &self, + payment_data: &D, + ) -> CustomResult, errors::ApiErrorResponse> { + let apple_pay_wallet_data = payment_data + .get_payment_method_data() + .and_then(|payment_method_data| payment_method_data.get_wallet_data()) + .and_then(|wallet_data| wallet_data.get_apple_pay_wallet_data()) + .get_required_value("Apple Pay wallet token") + .attach_printable( + "Apple Pay wallet data not found in the payment method data during the Apple Pay predecryption flow", + )?; + + match &apple_pay_wallet_data.payment_data { + common_payments_types::ApplePayPaymentData::Encrypted(_) => Ok(None), + common_payments_types::ApplePayPaymentData::Decrypted(apple_pay_predecrypt_data) => { + helpers::validate_card_expiry( + &apple_pay_predecrypt_data.application_expiration_month, + &apple_pay_predecrypt_data.application_expiration_year, + )?; + Ok(Some(PaymentMethodToken::ApplePayDecrypt(Box::new( + apple_pay_predecrypt_data.clone(), + )))) + } + } + } + fn decide_wallet_flow( &self, state: &SessionState, @@ -5028,9 +5071,10 @@ where .get_payment_method_data() .and_then(|payment_method_data| payment_method_data.get_wallet_data()) .and_then(|wallet_data| wallet_data.get_apple_pay_wallet_data()) - .get_required_value("Paze wallet token").attach_printable( + .get_required_value("Apple Pay wallet token").attach_printable( "Apple Pay wallet data not found in the payment method data during the Apple Pay decryption flow", )?; + let apple_pay_data = ApplePayData::token_json(domain::WalletData::ApplePay(apple_pay_wallet_data.clone())) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -5043,15 +5087,22 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed to decrypt apple pay token")?; - let apple_pay_predecrypt = apple_pay_data - .parse_value::( - "ApplePayPredecryptData", + let apple_pay_predecrypt_internal = apple_pay_data + .parse_value::( + "ApplePayPredecryptDataInternal", ) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable( "failed to parse decrypted apple pay response to ApplePayPredecryptData", )?; + let apple_pay_predecrypt = + common_types::payments::ApplePayPredecryptData::try_from(apple_pay_predecrypt_internal) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to convert ApplePayPredecryptDataInternal to ApplePayPredecryptData", + )?; + Ok(PaymentMethodToken::ApplePayDecrypt(Box::new( apple_pay_predecrypt, ))) diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 3a51c628b82..c7fe77e46a0 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -47,10 +47,10 @@ use hyperswitch_domain_models::router_flow_types::{ pub use hyperswitch_domain_models::{ payment_address::PaymentAddress, router_data::{ - AccessToken, AdditionalPaymentMethodConnectorResponse, ApplePayCryptogramData, - ApplePayPredecryptData, ConnectorAuthType, ConnectorResponseData, ErrorResponse, - GooglePayDecryptedData, GooglePayPaymentMethodDetails, PaymentMethodBalance, - PaymentMethodToken, RecurringMandatePaymentData, RouterData, + AccessToken, AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, + ConnectorResponseData, ErrorResponse, GooglePayDecryptedData, + GooglePayPaymentMethodDetails, PaymentMethodBalance, PaymentMethodToken, + RecurringMandatePaymentData, RouterData, }, router_data_v2::{ AccessTokenFlowData, DisputesFlowData, ExternalAuthenticationFlowData, FilesFlowData, diff --git a/crates/router/tests/connectors/worldpay.rs b/crates/router/tests/connectors/worldpay.rs index 451aa398eb7..9909a9498f2 100644 --- a/crates/router/tests/connectors/worldpay.rs +++ b/crates/router/tests/connectors/worldpay.rs @@ -100,7 +100,9 @@ async fn should_authorize_applepay_payment() { Some(types::PaymentsAuthorizeData { payment_method_data: domain::PaymentMethodData::Wallet( domain::WalletData::ApplePay(domain::ApplePayWalletData { - payment_data: "someData".to_string(), + payment_data: common_types::payments::ApplePayPaymentData::Encrypted( + "someData".to_string(), + ), transaction_identifier: "someId".to_string(), payment_method: domain::ApplepayPaymentMethod { display_name: "someName".to_string(),