feat: Apple pay session flow integrate (#138)

This commit is contained in:
Sangamesh Kulkarni
2022-12-14 17:59:27 +05:30
committed by GitHub
parent 1f0d1deb2b
commit 8b8ff8188f
29 changed files with 495 additions and 33 deletions

View File

@ -37,8 +37,8 @@ locker_decryption_key1 = ""
locker_decryption_key2 = ""
[connectors.supported]
wallets = ["klarna","braintree"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree","aci"]
[eph_key]
validity = 1
@ -64,6 +64,9 @@ base_url = "https://api.sandbox.braintreegateway.com/"
[connectors.klarna]
base_url = "https://api-na.playground.klarna.com/"
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
[scheduler]
stream = "SCHEDULER_STREAM"
consumer_group = "SCHEDULER_GROUP"

View File

@ -116,9 +116,12 @@ base_url = "https://api.sandbox.braintreegateway.com/"
[connectors.klarna]
base_url = "https://api-na.playground.klarna.com/"
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
# This data is used to call respective connectors for wallets and cards
[connectors.supported]
wallets = ["klarna","braintree"]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]

View File

@ -71,7 +71,9 @@ base_url = "https://api.sandbox.braintreegateway.com/"
[connectors.klarna]
base_url = "https://api-na.playground.klarna.com/"
[connectors.supported]
wallets = ["klarna","braintree"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
[connectors.supported]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]

View File

@ -703,6 +703,19 @@ pub enum SessionToken {
Paypal {
session_token: String,
},
Applepay {
epoch_timestamp: u64,
expires_at: u64,
merchant_session_identifier: String,
nonce: String,
merchant_identifier: String,
domain_name: String,
display_name: String,
signature: String,
operational_analytics_identifier: String,
retries: u8,
psp_id: String,
},
}
#[derive(Default, Debug, serde::Serialize, Clone)]

View File

@ -49,7 +49,7 @@ mime = "0.3.16"
nanoid = "0.4.0"
once_cell = "1.16.0"
rand = "0.8.5"
reqwest = { version = "0.11.12", features = ["json"] }
reqwest = { version = "0.11.12", features = ["json", "native-tls"] }
ring = "0.16.20"
serde = { version = "1.0.149", features = ["derive"] }
serde_json = "1.0.89"

View File

@ -114,6 +114,7 @@ pub struct Connectors {
pub braintree: ConnectorParams,
pub klarna: ConnectorParams,
pub supported: SupportedConnectors,
pub applepay: ConnectorParams,
}
#[derive(Debug, Deserialize, Clone)]

View File

@ -1,5 +1,6 @@
pub mod aci;
pub mod adyen;
pub mod applepay;
pub mod authorizedotnet;
pub mod braintree;
pub mod checkout;
@ -7,6 +8,6 @@ pub mod klarna;
pub mod stripe;
pub use self::{
aci::Aci, adyen::Adyen, authorizedotnet::Authorizedotnet, braintree::Braintree,
checkout::Checkout, klarna::Klarna, stripe::Stripe,
aci::Aci, adyen::Adyen, applepay::Applepay, authorizedotnet::Authorizedotnet,
braintree::Braintree, checkout::Checkout, klarna::Klarna, stripe::Stripe,
};

View File

@ -0,0 +1,246 @@
mod transformers;
use std::fmt::Debug;
use bytes::Bytes;
use common_utils::ext_traits::ValueExt;
use error_stack::{IntoReport, ResultExt};
use self::transformers as applepay;
use crate::{
configs::settings,
core::errors::{self, CustomResult},
headers, services,
types::{
self,
api::{self, ConnectorCommon},
},
utils::{self, BytesExt, OptionExt},
};
#[derive(Debug, Clone)]
pub struct Applepay;
impl api::ConnectorCommon for Applepay {
fn id(&self) -> &'static str {
"applepay"
}
fn base_url(&self, connectors: settings::Connectors) -> String {
connectors.applepay.base_url
}
}
impl api::Payment for Applepay {}
impl api::PaymentAuthorize for Applepay {}
impl api::PaymentSync for Applepay {}
impl api::PaymentVoid for Applepay {}
impl api::PaymentCapture for Applepay {}
impl api::PreVerify for Applepay {}
impl api::PaymentSession for Applepay {}
impl
services::ConnectorIntegration<
api::Verify,
types::VerifyRequestData,
types::PaymentsResponseData,
> for Applepay
{
}
impl
services::ConnectorIntegration<
api::Capture,
types::PaymentsCaptureData,
types::PaymentsResponseData,
> for Applepay
{
}
impl
services::ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
for Applepay
{
}
impl
services::ConnectorIntegration<
api::Authorize,
types::PaymentsAuthorizeData,
types::PaymentsResponseData,
> for Applepay
{
}
impl
services::ConnectorIntegration<
api::Void,
types::PaymentsCancelData,
types::PaymentsResponseData,
> for Applepay
{
}
#[async_trait::async_trait]
impl
services::ConnectorIntegration<
api::Session,
types::PaymentsSessionData,
types::PaymentsResponseData,
> for Applepay
{
fn get_headers(
&self,
_req: &types::PaymentsSessionRouterData,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
let header = vec![(
headers::CONTENT_TYPE.to_string(),
types::PaymentsSessionType::get_content_type(self).to_string(),
)];
Ok(header)
}
fn get_url(
&self,
_req: &types::PaymentsSessionRouterData,
connectors: settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!(
"{}{}",
self.base_url(connectors),
"paymentservices/paymentSession"
))
}
fn get_request_body(
&self,
req: &types::PaymentsSessionRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> {
let req = utils::Encode::<applepay::ApplepaySessionRequest>::convert_and_encode(req)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(req))
}
fn build_request(
&self,
req: &types::PaymentsSessionRouterData,
connectors: settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
let request = services::RequestBuilder::new()
.method(services::Method::Post)
// TODO: [ORCA-346] Requestbuilder needs &str migrate get_url to send &str instead of owned string
.url(&types::PaymentsSessionType::get_url(self, req, connectors)?)
.headers(types::PaymentsSessionType::get_headers(self, req)?)
.body(types::PaymentsSessionType::get_request_body(self, req)?)
.add_certificate(types::PaymentsSessionType::get_certificate(self, req)?)
.add_certificate_key(types::PaymentsSessionType::get_certificate_key(self, req)?)
.build();
Ok(Some(request))
}
fn handle_response(
&self,
data: &types::PaymentsSessionRouterData,
res: types::Response,
) -> CustomResult<types::PaymentsSessionRouterData, errors::ConnectorError> {
let response: applepay::ApplepaySessionResponse = res
.response
.parse_struct("ApplepaySessionResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
.change_context(errors::ConnectorError::ResponseHandlingFailed)
}
fn get_error_response(
&self,
res: Bytes,
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
let response: applepay::ErrorResponse = res
.parse_struct("ErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Ok(types::ErrorResponse {
code: response.status_code,
message: response.status_message,
reason: None,
})
}
fn get_certificate(
&self,
req: &types::PaymentsSessionRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> {
let metadata = req
.connector_meta_data
.to_owned()
.get_required_value("connector_meta_data")
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
let session_object: transformers::SessionObject = metadata
.parse_value("SessionObject")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(session_object.certificate))
}
fn get_certificate_key(
&self,
req: &types::PaymentsSessionRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> {
let metadata = req
.connector_meta_data
.to_owned()
.get_required_value("connector_meta_data")
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
let session_object: transformers::SessionObject = metadata
.parse_value("SessionObject")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(session_object.certificate_keys))
}
}
impl api::Refund for Applepay {}
impl api::RefundExecute for Applepay {}
impl api::RefundSync for Applepay {}
impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData>
for Applepay
{
}
impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
for Applepay
{
}
impl services::ConnectorRedirectResponse for Applepay {}
#[async_trait::async_trait]
impl api::IncomingWebhook for Applepay {
fn get_webhook_object_reference_id(
&self,
_body: &[u8],
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
}
fn get_webhook_event_type(
&self,
_body: &[u8],
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
}
fn get_webhook_resource_object(
&self,
_body: &[u8],
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
Err(errors::ConnectorError::WebhooksNotImplemented).into_report()
}
}

View File

@ -0,0 +1,103 @@
use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;
use masking::{Deserialize, Serialize};
use crate::{core::errors, types, utils::OptionExt};
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplepaySessionRequest {
merchant_identifier: String,
display_name: String,
initiative: String,
initiative_context: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplepaySessionResponse {
epoch_timestamp: u64,
expires_at: u64,
merchant_session_identifier: String,
nonce: String,
merchant_identifier: String,
domain_name: String,
display_name: String,
signature: String,
operational_analytics_identifier: String,
retries: u8,
psp_id: String,
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
pub status_code: String,
pub status_message: String,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SessionObject {
pub certificate: String,
pub certificate_keys: String,
pub merchant_identifier: String,
pub display_name: String,
pub initiative: String,
pub initiative_context: String,
}
impl TryFrom<&types::PaymentsSessionRouterData> for ApplepaySessionRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsSessionRouterData) -> Result<Self, Self::Error> {
let metadata = item
.connector_meta_data
.to_owned()
.get_required_value("connector_meta_data")
.change_context(errors::ConnectorError::NoConnectorMetaData)?;
let session_object: SessionObject = metadata
.parse_value("SessionObject")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Self {
merchant_identifier: session_object.merchant_identifier,
display_name: session_object.display_name,
initiative: session_object.initiative,
initiative_context: session_object.initiative_context,
})
}
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, ApplepaySessionResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ParsingError>;
fn try_from(
item: types::ResponseRouterData<F, ApplepaySessionResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
Ok(types::RouterData {
//TODO : change in session response to fit apple pay session object
response: Ok(types::PaymentsResponseData::SessionResponse {
session_token: {
api_models::payments::SessionToken::Applepay {
epoch_timestamp: item.response.epoch_timestamp,
expires_at: item.response.expires_at,
merchant_session_identifier: item.response.merchant_session_identifier,
nonce: item.response.nonce,
merchant_identifier: item.response.merchant_identifier,
domain_name: item.response.domain_name,
display_name: item.response.display_name,
signature: item.response.signature,
operational_analytics_identifier: item
.response
.operational_analytics_identifier,
retries: item.response.retries,
psp_id: item.response.psp_id,
}
},
}),
..item.data
})
}
}

View File

@ -321,6 +321,7 @@ pub async fn create_payment_connector(
payment_methods_enabled,
test_mode: req.test_mode,
disabled: req.disabled,
metadata: req.metadata,
};
let mca = store

View File

@ -234,6 +234,8 @@ pub enum ApiClientError {
InvalidProxyConfiguration,
#[error("Client construction failed")]
ClientConstructionFailed,
#[error("Certificate decode failed")]
CertificateDecodeFailed,
#[error("URL encoding of request payload failed")]
UrlEncodingFailed,
@ -295,6 +297,12 @@ pub enum ConnectorError {
MissingRequiredField { field_name: String },
#[error("Failed to obtain authentication type")]
FailedToObtainAuthType,
#[error("Failed to obtain certificate")]
FailedToObtainCertificate,
#[error("Connector meta data not found")]
NoConnectorMetaData,
#[error("Failed to obtain certificate key")]
FailedToObtainCertificateKey,
#[error("This step has not been implemented for: {0}")]
NotImplemented(String),
#[error("Missing connector transaction ID")]

View File

@ -183,7 +183,6 @@ where
Op: Operation<F, Req> + Send + Sync + Clone,
Req: Debug,
Res: transformers::ToResponse<Req, PaymentData<F>, Op> + From<Req>,
// To create connector flow specific interface data
PaymentData<F>: ConstructFlowSpecificData<F, FData, types::PaymentsResponseData>,
types::RouterData<F, FData, types::PaymentsResponseData>: Feature<F, FData>,

View File

@ -95,7 +95,7 @@ where
.payment_attempt
.authentication_type
.unwrap_or_default(),
connector_meta_data: merchant_connector_account.metadata,
request: T::try_from(payment_data.clone())?,
response: response.map_or_else(|| Err(types::ErrorResponse::default()), Ok),
};

View File

@ -71,6 +71,7 @@ pub async fn construct_refund_router_data<'a, F>(
// Does refund need shipping/billing address ?
address: PaymentAddress::default(),
auth_type: payment_attempt.authentication_type.unwrap_or_default(),
connector_meta_data: None,
request: types::RefundsData {
refund_id: refund.refund_id.clone(),

View File

@ -168,6 +168,7 @@ impl MerchantConnectorAccountInterface for MockDb {
disabled: t.disabled,
merchant_connector_id: t.merchant_connector_id.unwrap_or_default(),
payment_methods_enabled: t.payment_methods_enabled,
metadata: t.metadata,
connector_type: t
.connector_type
.unwrap_or(crate::types::storage::enums::ConnectorType::FinOperations),

View File

@ -104,6 +104,20 @@ pub trait ConnectorIntegration<T, Req, Resp>: ConnectorIntegrationExt<T, Req, Re
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
Ok(ErrorResponse::get_not_implemented())
}
fn get_certificate(
&self,
_req: &types::RouterData<T, Req, Resp>,
) -> CustomResult<Option<String>, errors::ConnectorError> {
Ok(None)
}
fn get_certificate_key(
&self,
_req: &types::RouterData<T, Req, Resp>,
) -> CustomResult<Option<String>, errors::ConnectorError> {
Ok(None)
}
}
#[instrument(skip_all)]
@ -196,9 +210,10 @@ async fn send_request(
&state.conf.proxy,
should_bypass_proxy,
crate::consts::REQUEST_TIME_OUT,
request.certificate,
request.certificate_key,
)?;
let headers = request.headers.construct_header_map()?;
match request.method {
Method::Get => client.get(url).add_headers(headers).send().await,
Method::Post => {

View File

@ -36,6 +36,8 @@ pub(super) fn create_client(
proxy: &Proxy,
should_bypass_proxy: bool,
request_time_out: u64,
client_certificate: Option<String>,
client_certificate_key: Option<String>,
) -> CustomResult<reqwest::Client, errors::ApiClientError> {
let mut client_builder = reqwest::Client::builder();
@ -58,6 +60,36 @@ pub(super) fn create_client(
}
}
client_builder = match (client_certificate, client_certificate_key) {
(Some(encoded_cert), Some(encoded_cert_key)) => {
let decoded_cert = base64::decode(encoded_cert)
.into_report()
.change_context(errors::ApiClientError::CertificateDecodeFailed)?;
let decoded_cert_key = base64::decode(encoded_cert_key)
.into_report()
.change_context(errors::ApiClientError::CertificateDecodeFailed)?;
let certificate = String::from_utf8(decoded_cert)
.into_report()
.change_context(errors::ApiClientError::CertificateDecodeFailed)?;
let certificate_key = String::from_utf8(decoded_cert_key)
.into_report()
.change_context(errors::ApiClientError::CertificateDecodeFailed)?;
let identity = reqwest::Identity::from_pkcs8_pem(
certificate.as_bytes(),
certificate_key.as_bytes(),
)
.into_report()
.change_context(errors::ApiClientError::CertificateDecodeFailed)?;
client_builder.identity(identity)
}
_ => client_builder,
};
let duration = Duration::from_secs(request_time_out);
client_builder = client_builder.timeout(duration);

View File

@ -37,6 +37,8 @@ pub struct Request {
pub payload: Option<Secret<String>>,
pub method: Method,
pub content_type: Option<ContentType>,
pub certificate: Option<String>,
pub certificate_key: Option<String>,
}
impl Request {
@ -47,6 +49,8 @@ impl Request {
headers: Vec::new(),
payload: None,
content_type: None,
certificate: None,
certificate_key: None,
}
}
@ -62,6 +66,14 @@ impl Request {
pub fn add_content_type(&mut self, content_type: ContentType) {
self.content_type = Some(content_type);
}
pub fn add_certificate(&mut self, certificate: Option<String>) {
self.certificate = certificate;
}
pub fn add_certificate_key(&mut self, certificate_key: Option<String>) {
self.certificate = certificate_key;
}
}
pub struct RequestBuilder {
@ -70,6 +82,8 @@ pub struct RequestBuilder {
pub payload: Option<Secret<String>>,
pub method: Method,
pub content_type: Option<ContentType>,
pub certificate: Option<String>,
pub certificate_key: Option<String>,
}
impl RequestBuilder {
@ -80,6 +94,8 @@ impl RequestBuilder {
headers: Vec::new(),
payload: None,
content_type: None,
certificate: None,
certificate_key: None,
}
}
@ -115,6 +131,16 @@ impl RequestBuilder {
self
}
pub fn add_certificate(mut self, certificate: Option<String>) -> RequestBuilder {
self.certificate = certificate;
self
}
pub fn add_certificate_key(mut self, certificate_key: Option<String>) -> RequestBuilder {
self.certificate_key = certificate_key;
self
}
pub fn build(self) -> Request {
Request {
method: self.method,
@ -122,6 +148,8 @@ impl RequestBuilder {
headers: self.headers,
payload: self.payload,
content_type: self.content_type,
certificate: self.certificate,
certificate_key: self.certificate_key,
}
}
}

View File

@ -48,14 +48,14 @@ pub type PaymentsSyncType =
dyn services::ConnectorIntegration<api::PSync, PaymentsSyncData, PaymentsResponseData>;
pub type PaymentsCaptureType =
dyn services::ConnectorIntegration<api::Capture, PaymentsCaptureData, PaymentsResponseData>;
pub type PaymentsSessionType =
dyn services::ConnectorIntegration<api::Session, PaymentsSessionData, PaymentsResponseData>;
pub type PaymentsVoidType =
dyn services::ConnectorIntegration<api::Void, PaymentsCancelData, PaymentsResponseData>;
pub type RefundExecuteType =
dyn services::ConnectorIntegration<api::Execute, RefundsData, RefundsResponseData>;
pub type RefundSyncType =
dyn services::ConnectorIntegration<api::RSync, RefundsData, RefundsResponseData>;
pub type PaymentsSessionType =
dyn services::ConnectorIntegration<api::Session, PaymentsSessionData, PaymentsResponseData>;
pub type VerifyRouterData = RouterData<api::Verify, VerifyRequestData, PaymentsResponseData>;
@ -73,6 +73,7 @@ pub struct RouterData<Flow, Request, Response> {
pub orca_return_url: Option<String>,
pub address: PaymentAddress,
pub auth_type: storage_enums::AuthenticationType,
pub connector_meta_data: Option<serde_json::Value>,
/// Contains flow-specific data required to construct a request and send it to the connector.
pub request: Request,
@ -123,17 +124,11 @@ pub struct PaymentsCancelData {
#[derive(Debug, Clone)]
pub struct PaymentsSessionData {
//TODO: Add the fields here as required
pub amount: i32,
pub currency: storage_enums::Currency,
}
#[derive(Debug, Clone)]
pub struct ConnectorSessionToken {
pub connector_name: String,
pub session_id: Option<String>,
pub session_token: String,
}
#[derive(Debug, Clone)]
pub struct VerifyRequestData {
pub payment_method_data: payments::PaymentMethod,
@ -250,7 +245,7 @@ pub struct ResponseRouterData<Flow, R, Request, Response> {
}
// Different patterns of authentication.
#[derive(Debug, Clone, serde::Deserialize)]
#[derive(Default, Debug, Clone, serde::Deserialize)]
#[serde(tag = "auth_type")]
pub enum ConnectorAuthType {
HeaderKey {
@ -265,15 +260,10 @@ pub enum ConnectorAuthType {
key1: String,
api_secret: String,
},
#[default]
NoKey,
}
impl Default for ConnectorAuthType {
fn default() -> Self {
Self::HeaderKey {
api_key: "".to_string(),
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ConnectorsList {
pub connectors: Vec<String>,

View File

@ -103,6 +103,7 @@ impl ConnectorData {
"authorizedotnet" => Ok(Box::new(&connector::Authorizedotnet)),
"braintree" => Ok(Box::new(&connector::Braintree)),
"klarna" => Ok(Box::new(&connector::Klarna)),
"applepay" => Ok(Box::new(&connector::Applepay)),
_ => Err(report!(errors::UnexpectedError)
.attach_printable(format!("invalid connector name: {connector_name}")))
.change_context(errors::ConnectorError::InvalidConnectorName)

View File

@ -8,6 +8,7 @@ pub enum Connector {
Authorizedotnet,
Braintree,
Klarna,
Applepay,
#[default]
Dummy,
}

View File

@ -51,6 +51,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
response: Err(types::ErrorResponse::default()),
payment_method_id: None,
address: PaymentAddress::default(),
connector_meta_data: None,
}
}
@ -89,6 +90,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
payment_method_id: None,
response: Err(types::ErrorResponse::default()),
address: PaymentAddress::default(),
connector_meta_data: None,
}
}

View File

@ -51,6 +51,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
payment_method_id: None,
response: Err(types::ErrorResponse::default()),
address: PaymentAddress::default(),
connector_meta_data: None,
}
}
@ -61,6 +62,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
types::RouterData {
flow: PhantomData,
connector_meta_data: None,
merchant_id: String::from("authorizedotnet"),
connector: "authorizedotnet".to_string(),
payment_id: uuid::Uuid::new_v4().to_string(),

View File

@ -48,6 +48,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData {
response: Err(types::ErrorResponse::default()),
payment_method_id: None,
address: PaymentAddress::default(),
connector_meta_data: None,
}
}
@ -58,6 +59,7 @@ fn construct_refund_router_data<F>() -> types::RefundsRouterData<F> {
types::RouterData {
flow: PhantomData,
connector_meta_data: None,
merchant_id: "checkout".to_string(),
connector: "checkout".to_string(),
payment_id: uuid::Uuid::new_v4().to_string(),

View File

@ -16,6 +16,7 @@ pub struct MerchantConnectorAccount {
#[diesel(deserialize_as = super::OptionalDieselArray<serde_json::Value>)]
pub payment_methods_enabled: Option<Vec<serde_json::Value>>,
pub connector_type: storage_enums::ConnectorType,
pub metadata: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Default, Insertable, router_derive::DebugAsDisplay)]
@ -29,6 +30,7 @@ pub struct MerchantConnectorAccountNew {
pub disabled: Option<bool>,
pub merchant_connector_id: Option<i32>,
pub payment_methods_enabled: Option<Vec<serde_json::Value>>,
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug)]

View File

@ -175,6 +175,7 @@ diesel::table! {
merchant_connector_id -> Int4,
payment_methods_enabled -> Nullable<Array<Nullable<Json>>>,
connector_type -> ConnectorType,
metadata -> Nullable<Jsonb>,
}
}

View File

@ -58,10 +58,12 @@ base_url = "http://stripe-mock:12111/"
[connectors.braintree]
base_url = "https://api.sandbox.braintreegateway.com/"
[connectors.applepay]
base_url = "https://apple-pay-gateway.apple.com/"
[connectors.klarna]
base_url = "https://api-na.playground.klarna.com/"
[connectors.supported]
wallets = ["klarna","braintree"]
wallets = ["klarna","braintree","applepay"]
cards = ["stripe","adyen","authorizedotnet","checkout","braintree"]

View File

@ -0,0 +1 @@
ALTER TABLE merchant_connector_account DROP COLUMN metadata;

View File

@ -0,0 +1 @@
ALTER TABLE merchant_connector_account ADD COLUMN metadata JSONB DEFAULT NULL;