Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): google pay decrypt flow #6991

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -6870,6 +6870,11 @@
"type": "object",
"description": "This field contains the Paze certificates and credentials",
"nullable": true
},
"google_pay": {
"type": "object",
"description": "This field contains the Google Pay certificates and credentials\n#[serde(skip_serializing_if = \"Option::is_none\")]",
"nullable": true
}
},
"additionalProperties": false
Expand Down
5 changes: 5 additions & 0 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -9523,6 +9523,11 @@
"type": "object",
"description": "This field contains the Paze certificates and credentials",
"nullable": true
},
"google_pay": {
"type": "object",
"description": "This field contains the Google Pay certificates and credentials\n#[serde(skip_serializing_if = \"Option::is_none\")]",
"nullable": true
}
},
"additionalProperties": false
Expand Down
5 changes: 5 additions & 0 deletions config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,11 @@ apple_pay_merchant_cert_key = "APPLE_PAY_MERCHNAT_CERTIFICATE_KEY" # Private key
paze_private_key = "PAZE_PRIVATE_KEY" # Base 64 Encoded Private Key File cakey.pem generated for Paze -> Command to create private key: openssl req -newkey rsa:2048 -x509 -keyout cakey.pem -out cacert.pem -days 365
paze_private_key_passphrase = "PAZE_PRIVATE_KEY_PASSPHRASE" # PEM Passphrase used for generating Private Key File cakey.pem

[google_pay_decrypt_keys]
google_pay_root_signing_keys = "GOOGLE_PAY_ROOT_SIGNING_KEYS" # Base 64 Encoded Root Signing Keys provided by Google Pay (https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography)
google_pay_recipient_id = "GOOGLE_PAY_RECIPIENT_ID" # Recipient ID provided by Google Pay
google_pay_private_key = "GOOGLE_PAY_PRIVATE_KEY" # Private key generated by RSA:2048 algorithm

[applepay_merchant_configs]
# Run below command to get common merchant identifier for applepay in shell
#
Expand Down
5 changes: 5 additions & 0 deletions config/deployments/env_specific.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ apple_pay_merchant_cert_key = "APPLE_PAY_MERCHNAT_CERTIFICATE_KEY" # Private key
paze_private_key = "PAZE_PRIVATE_KEY" # Base 64 Encoded Private Key File cakey.pem generated for Paze -> Command to create private key: openssl req -newkey rsa:2048 -x509 -keyout cakey.pem -out cacert.pem -days 365
paze_private_key_passphrase = "PAZE_PRIVATE_KEY_PASSPHRASE" # PEM Passphrase used for generating Private Key File cakey.pem

[google_pay_decrypt_keys]
google_pay_root_signing_keys = "GOOGLE_PAY_ROOT_SIGNING_KEYS" # Base 64 Encoded Root Signing Keys provided by Google Pay (https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography)
google_pay_recipient_id = "GOOGLE_PAY_RECIPIENT_ID" # Recipient ID provided by Google Pay
google_pay_private_key = "GOOGLE_PAY_PRIVATE_KEY" # Private key generated by RSA:2048 algorithm

[applepay_merchant_configs]
common_merchant_identifier = "APPLE_PAY_COMMON_MERCHANT_IDENTIFIER" # Refer to config.example.toml to learn how you can generate this value
merchant_cert = "APPLE_PAY_MERCHANT_CERTIFICATE" # Merchant Certificate provided by Apple Pay (https://developer.apple.com/) Certificates, Identifiers & Profiles > Apple Pay Merchant Identity Certificate
Expand Down
5 changes: 5 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,11 @@ apple_pay_merchant_cert_key = "APPLE_PAY_MERCHNAT_CERTIFICATE_KEY"
paze_private_key = "PAZE_PRIVATE_KEY"
paze_private_key_passphrase = "PAZE_PRIVATE_KEY_PASSPHRASE"

[google_pay_decrypt_keys]
google_pay_root_signing_keys = "GOOGLE_PAY_ROOT_SIGNING_KEYS" # Base 64 Encoded Root Signing Keys provided by Google Pay (https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography)
google_pay_recipient_id = "GOOGLE_PAY_RECIPIENT_ID" # Recipient ID provided by Google Pay
google_pay_private_key = "GOOGLE_PAY_PRIVATE_KEY" # Private key generated by RSA:2048 algorithm

[generic_link]
[generic_link.payment_method_collect]
sdk_url = "http://localhost:9050/HyperLoader.js"
Expand Down
4 changes: 4 additions & 0 deletions crates/api_models/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,10 @@ pub struct ConnectorWalletDetails {
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<Object>)]
pub paze: Option<pii::SecretSerdeValue>,
/// This field contains the Google Pay certificates and credentials
/// #[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<Object>)]
pub google_pay: Option<pii::SecretSerdeValue>,
}

/// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc."
Expand Down
24 changes: 24 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3650,3 +3650,27 @@ pub enum FeatureStatus {
NotSupported,
Supported,
}

#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
strum::Display,
strum::VariantNames,
strum::EnumIter,
strum::EnumString,
Deserialize,
Serialize,
)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GooglePayAuthMethod {
/// Contain pan data only
PanOnly,
/// Contain cryptogram data along with pan data
#[serde(rename = "CRYPTOGRAM_3DS")]
Cryptogram,
}
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,12 @@ impl TryFrom<&BankOfAmericaRouterData<&PaymentsAuthorizeRouterData>>
PaymentMethodToken::PazeDecrypt(_) => Err(
unimplemented_payment_method!("Paze", "Bank Of America"),
)?,
PaymentMethodToken::GooglePayDecrypt(_) => {
Err(unimplemented_payment_method!(
"Google Pay",
"Bank Of America"
))?
}
},
None => {
let email = item.router_data.request.get_email()?;
Expand Down Expand Up @@ -2279,6 +2285,10 @@ impl TryFrom<(&SetupMandateRouterData, ApplePayWalletData)> for BankOfAmericaPay
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Bank Of America"))?
}
PaymentMethodToken::GooglePayDecrypt(_) => Err(unimplemented_payment_method!(
"Google Pay",
"Bank Of America"
))?,
},
None => PaymentInformation::from(&apple_pay_data),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use hyperswitch_domain_models::{
},
router_data::{
AdditionalPaymentMethodConnectorResponse, ApplePayPredecryptData, ConnectorAuthType,
ConnectorResponseData, ErrorResponse, PaymentMethodToken, RouterData,
ConnectorResponseData, ErrorResponse, GooglePayDecryptedData, PaymentMethodToken,
RouterData,
},
router_flow_types::{
payments::Authorize,
Expand Down Expand Up @@ -216,6 +217,9 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Cybersource"))?
}
PaymentMethodToken::GooglePayDecrypt(_) => {
Err(unimplemented_payment_method!("Google Pay", "Cybersource"))?
}
},
None => (
PaymentInformation::ApplePayToken(Box::new(
Expand All @@ -233,15 +237,17 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
),
},
WalletData::GooglePay(google_pay_data) => (
PaymentInformation::GooglePay(Box::new(GooglePayPaymentInformation {
fluid_data: FluidData {
value: Secret::from(
consts::BASE64_ENGINE
.encode(google_pay_data.tokenization_data.token),
),
descriptor: None,
PaymentInformation::GooglePayToken(Box::new(
GooglePayTokenPaymentInformation {
fluid_data: FluidData {
value: Secret::from(
consts::BASE64_ENGINE
.encode(google_pay_data.tokenization_data.token),
),
descriptor: None,
},
},
})),
)),
Some(PaymentSolution::GooglePay),
),
WalletData::AliPayQr(_)
Expand Down Expand Up @@ -490,10 +496,16 @@ pub const FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY: &str = "FID=COMMON.SAMSUNG.INAP

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayPaymentInformation {
pub struct GooglePayTokenPaymentInformation {
fluid_data: FluidData,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooglePayPaymentInformation {
tokenized_card: TokenizedCard,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SamsungPayTokenizedCard {
Expand All @@ -519,6 +531,7 @@ pub struct SamsungPayFluidDataValue {
#[serde(untagged)]
pub enum PaymentInformation {
Cards(Box<CardPaymentInformation>),
GooglePayToken(Box<GooglePayTokenPaymentInformation>),
GooglePay(Box<GooglePayPaymentInformation>),
ApplePay(Box<ApplePayPaymentInformation>),
ApplePayToken(Box<ApplePayTokenPaymentInformation>),
Expand Down Expand Up @@ -587,6 +600,8 @@ pub enum TransactionType {
ApplePay,
#[serde(rename = "1")]
SamsungPay,
#[serde(rename = "1")]
GooglePay,
}

impl From<PaymentSolution> for String {
Expand Down Expand Up @@ -1706,7 +1721,7 @@ impl
let order_information = OrderInformationWithBill::from((item, Some(bill_to)));

let payment_information =
PaymentInformation::GooglePay(Box::new(GooglePayPaymentInformation {
PaymentInformation::GooglePayToken(Box::new(GooglePayTokenPaymentInformation {
fluid_data: FluidData {
value: Secret::from(
consts::BASE64_ENGINE.encode(google_pay_data.tokenization_data.token),
Expand Down Expand Up @@ -1735,6 +1750,71 @@ impl
}
}

impl
TryFrom<(
&CybersourceRouterData<&PaymentsAuthorizeRouterData>,
Box<GooglePayDecryptedData>,
GooglePayWalletData,
)> for CybersourcePaymentsRequest
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
(item, google_pay_decrypted_data, google_pay_data): (
&CybersourceRouterData<&PaymentsAuthorizeRouterData>,
Box<GooglePayDecryptedData>,
GooglePayWalletData,
),
) -> Result<Self, Self::Error> {
let email = item
.router_data
.get_billing_email()
.or(item.router_data.request.get_email())?;
let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?;
let order_information = OrderInformationWithBill::from((item, Some(bill_to)));

let payment_information =
PaymentInformation::ApplePay(Box::new(ApplePayPaymentInformation {
tokenized_card: TokenizedCard {
number: google_pay_decrypted_data.payment_method_details.pan,
cryptogram: google_pay_decrypted_data
.payment_method_details
.cryptogram
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "cryptogram",
})?,
transaction_type: TransactionType::GooglePay,
expiration_year: google_pay_decrypted_data
.payment_method_details
.expiration_year,
expiration_month: google_pay_decrypted_data
.payment_method_details
.expiration_month,
},
}));
let processing_information = ProcessingInformation::try_from((
item,
Some(PaymentSolution::GooglePay),
Some(google_pay_data.info.card_network),
))?;
let client_reference_information = ClientReferenceInformation::from(item);
let merchant_defined_information = item
.router_data
.request
.metadata
.clone()
.map(convert_metadata_to_merchant_defined_info);

Ok(Self {
processing_information,
payment_information,
order_information,
client_reference_information,
consumer_authentication_information: None,
merchant_defined_information,
})
}
}

impl
TryFrom<(
&CybersourceRouterData<&PaymentsAuthorizeRouterData>,
Expand Down Expand Up @@ -1849,6 +1929,9 @@ impl TryFrom<&CybersourceRouterData<&PaymentsAuthorizeRouterData>> for Cybersour
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Cybersource"))?
}
PaymentMethodToken::GooglePayDecrypt(_) => Err(
unimplemented_payment_method!("Google Pay", "Cybersource"),
)?,
},
None => {
let email = item
Expand Down Expand Up @@ -1916,7 +1999,31 @@ impl TryFrom<&CybersourceRouterData<&PaymentsAuthorizeRouterData>> for Cybersour
}
}
WalletData::GooglePay(google_pay_data) => {
Self::try_from((item, google_pay_data))
match item.router_data.payment_method_token.clone() {
Some(payment_method_token) => match payment_method_token {
PaymentMethodToken::GooglePayDecrypt(decrypt_data) => {
Self::try_from((item, decrypt_data, google_pay_data))
}
PaymentMethodToken::Token(_) => {
Err(unimplemented_payment_method!(
"Apple Pay",
"Manual",
"Cybersource"
))?
}
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Cybersource"))?
}
PaymentMethodToken::ApplePayDecrypt(_) => {
Err(unimplemented_payment_method!(
"Apple Pay",
"Simplified",
"Cybersource"
))?
}
},
None => Self::try_from((item, google_pay_data)),
}
}
WalletData::SamsungPay(samsung_pay_data) => {
Self::try_from((item, samsung_pay_data))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,9 @@ impl TryFrom<&FiuuRouterData<&PaymentsAuthorizeRouterData>> for FiuuPaymentReque
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Fiuu"))?
}
PaymentMethodToken::GooglePayDecrypt(_) => {
Err(unimplemented_payment_method!("Google Pay", "Fiuu"))?
}
}
}
WalletData::AliPayQr(_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,9 @@ impl TryFrom<&types::SetupMandateRouterData> for GocardlessMandateRequest {
let payment_method_token = item.get_payment_method_token()?;
let customer_bank_account = match payment_method_token {
PaymentMethodToken::Token(token) => Ok(token),
PaymentMethodToken::ApplePayDecrypt(_) | PaymentMethodToken::PazeDecrypt(_) => {
PaymentMethodToken::ApplePayDecrypt(_)
| PaymentMethodToken::PazeDecrypt(_)
| PaymentMethodToken::GooglePayDecrypt(_) => {
Err(errors::ConnectorError::NotImplemented(
"Setup Mandate flow for selected payment method through Gocardless".to_string(),
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ impl TryFrom<&MollieRouterData<&types::PaymentsAuthorizeRouterData>> for MollieP
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Mollie"))?
}
PaymentMethodToken::GooglePayDecrypt(_) => {
Err(unimplemented_payment_method!("Google Pay", "Mollie"))?
}
}),
},
)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for SquarePaymentsRequest {
PaymentMethodToken::PazeDecrypt(_) => {
Err(unimplemented_payment_method!("Paze", "Square"))?
}
PaymentMethodToken::GooglePayDecrypt(_) => {
Err(unimplemented_payment_method!("Google Pay", "Square"))?
}
},
amount_money: SquarePaymentsAmountData {
amount: item.request.amount,
Expand Down
Loading
Loading