mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(connector): Add connector cashtocode (#1429)
This commit is contained in:
@ -158,6 +158,7 @@ bambora.base_url = "https://api.na.bambora.com"
|
||||
bitpay.base_url = "https://test.bitpay.com"
|
||||
bluesnap.base_url = "https://sandbox.bluesnap.com/"
|
||||
braintree.base_url = "https://api.sandbox.braintreegateway.com/"
|
||||
cashtocode.base_url = "https://cluster05.api-test.cashtocode.com"
|
||||
checkout.base_url = "https://api.sandbox.checkout.com/"
|
||||
coinbase.base_url = "https://api.commerce.coinbase.com"
|
||||
cybersource.base_url = "https://apitest.cybersource.com/"
|
||||
@ -207,6 +208,7 @@ stripe = { banks = "alior_bank,bank_millennium,bank_nowy_bfg_sa,bank_pekao_sa,ba
|
||||
# This data is used to call respective connectors for wallets and cards
|
||||
[connectors.supported]
|
||||
wallets = ["klarna", "braintree", "applepay"]
|
||||
rewards = ["cashtocode"]
|
||||
cards = [
|
||||
"adyen",
|
||||
"authorizedotnet",
|
||||
|
||||
@ -53,6 +53,7 @@ tunnel_private_key = ""
|
||||
|
||||
[connectors.supported]
|
||||
wallets = ["klarna", "braintree", "applepay"]
|
||||
rewards = ["cashtocode",]
|
||||
cards = [
|
||||
"aci",
|
||||
"adyen",
|
||||
@ -112,6 +113,7 @@ bambora.base_url = "https://api.na.bambora.com"
|
||||
bitpay.base_url = "https://test.bitpay.com"
|
||||
bluesnap.base_url = "https://sandbox.bluesnap.com/"
|
||||
braintree.base_url = "https://api.sandbox.braintreegateway.com/"
|
||||
cashtocode.base_url = "https://cluster05.api-test.cashtocode.com"
|
||||
checkout.base_url = "https://api.sandbox.checkout.com/"
|
||||
coinbase.base_url = "https://api.commerce.coinbase.com"
|
||||
cybersource.base_url = "https://apitest.cybersource.com/"
|
||||
|
||||
@ -79,6 +79,7 @@ bambora.base_url = "https://api.na.bambora.com"
|
||||
bitpay.base_url = "https://test.bitpay.com"
|
||||
bluesnap.base_url = "https://sandbox.bluesnap.com/"
|
||||
braintree.base_url = "https://api.sandbox.braintreegateway.com/"
|
||||
cashtocode.base_url = "https://cluster05.api-test.cashtocode.com"
|
||||
checkout.base_url = "https://api.sandbox.checkout.com/"
|
||||
coinbase.base_url = "https://api.commerce.coinbase.com"
|
||||
cybersource.base_url = "https://apitest.cybersource.com/"
|
||||
@ -112,6 +113,7 @@ zen.base_url = "https://api.zen-test.com/"
|
||||
|
||||
[connectors.supported]
|
||||
wallets = ["klarna", "braintree", "applepay"]
|
||||
rewards = ["cashtocode",]
|
||||
cards = [
|
||||
"aci",
|
||||
"adyen",
|
||||
|
||||
@ -421,10 +421,13 @@ pub enum PaymentMethodType {
|
||||
BancontactCard,
|
||||
Becs,
|
||||
Blik,
|
||||
#[serde(rename = "classic")]
|
||||
ClassicReward,
|
||||
Credit,
|
||||
CryptoCurrency,
|
||||
Debit,
|
||||
Eps,
|
||||
Evoucher,
|
||||
Giropay,
|
||||
GooglePay,
|
||||
Ideal,
|
||||
@ -473,6 +476,7 @@ pub enum PaymentMethod {
|
||||
BankTransfer,
|
||||
Crypto,
|
||||
BankDebit,
|
||||
Reward,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@ -591,6 +595,7 @@ pub enum Connector {
|
||||
Bitpay,
|
||||
Bluesnap,
|
||||
Braintree,
|
||||
Cashtocode,
|
||||
Checkout,
|
||||
Coinbase,
|
||||
Cybersource,
|
||||
@ -688,6 +693,7 @@ pub enum RoutableConnectors {
|
||||
Bambora,
|
||||
Bluesnap,
|
||||
Braintree,
|
||||
Cashtocode,
|
||||
Checkout,
|
||||
Coinbase,
|
||||
Cybersource,
|
||||
|
||||
@ -589,6 +589,7 @@ pub enum PaymentMethodData {
|
||||
BankTransfer(Box<BankTransferData>),
|
||||
Crypto(CryptoData),
|
||||
MandatePayment,
|
||||
Reward(RewardData),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
@ -607,6 +608,7 @@ pub enum AdditionalPaymentData {
|
||||
Crypto {},
|
||||
BankDebit {},
|
||||
MandatePayment {},
|
||||
Reward {},
|
||||
}
|
||||
|
||||
impl From<&PaymentMethodData> for AdditionalPaymentData {
|
||||
@ -634,6 +636,7 @@ impl From<&PaymentMethodData> for AdditionalPaymentData {
|
||||
PaymentMethodData::Crypto(_) => Self::Crypto {},
|
||||
PaymentMethodData::BankDebit(_) => Self::BankDebit {},
|
||||
PaymentMethodData::MandatePayment => Self::MandatePayment {},
|
||||
PaymentMethodData::Reward(_) => Self::Reward {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -940,6 +943,13 @@ pub struct CardResponse {
|
||||
exp_year: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct RewardData {
|
||||
/// The merchant ID with which we have to call the connector
|
||||
pub merchant_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PaymentMethodDataResponse {
|
||||
@ -953,6 +963,7 @@ pub enum PaymentMethodDataResponse {
|
||||
Crypto(CryptoData),
|
||||
BankDebit(BankDebitData),
|
||||
MandatePayment,
|
||||
Reward(RewardData),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
@ -1582,6 +1593,7 @@ impl From<PaymentMethodData> for PaymentMethodDataResponse {
|
||||
PaymentMethodData::Crypto(crpto_data) => Self::Crypto(crpto_data),
|
||||
PaymentMethodData::BankDebit(bank_debit_data) => Self::BankDebit(bank_debit_data),
|
||||
PaymentMethodData::MandatePayment => Self::MandatePayment,
|
||||
PaymentMethodData::Reward(reward_data) => Self::Reward(reward_data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,6 +385,7 @@ pub struct Connectors {
|
||||
pub bitpay: ConnectorParams,
|
||||
pub bluesnap: ConnectorParams,
|
||||
pub braintree: ConnectorParams,
|
||||
pub cashtocode: ConnectorParams,
|
||||
pub checkout: ConnectorParams,
|
||||
pub coinbase: ConnectorParams,
|
||||
pub cybersource: ConnectorParams,
|
||||
|
||||
@ -6,6 +6,7 @@ pub mod bambora;
|
||||
pub mod bitpay;
|
||||
pub mod bluesnap;
|
||||
pub mod braintree;
|
||||
pub mod cashtocode;
|
||||
pub mod checkout;
|
||||
pub mod coinbase;
|
||||
pub mod cybersource;
|
||||
@ -40,10 +41,11 @@ pub mod zen;
|
||||
pub use self::dummyconnector::DummyConnector;
|
||||
pub use self::{
|
||||
aci::Aci, adyen::Adyen, airwallex::Airwallex, authorizedotnet::Authorizedotnet,
|
||||
bambora::Bambora, bitpay::Bitpay, bluesnap::Bluesnap, braintree::Braintree, checkout::Checkout,
|
||||
coinbase::Coinbase, cybersource::Cybersource, dlocal::Dlocal, fiserv::Fiserv, forte::Forte,
|
||||
globalpay::Globalpay, iatapay::Iatapay, klarna::Klarna, mollie::Mollie,
|
||||
multisafepay::Multisafepay, nexinets::Nexinets, nmi::Nmi, noon::Noon, nuvei::Nuvei,
|
||||
opennode::Opennode, payeezy::Payeezy, paypal::Paypal, payu::Payu, rapyd::Rapyd, shift4::Shift4,
|
||||
stripe::Stripe, trustpay::Trustpay, worldline::Worldline, worldpay::Worldpay, zen::Zen,
|
||||
bambora::Bambora, bitpay::Bitpay, bluesnap::Bluesnap, braintree::Braintree,
|
||||
cashtocode::Cashtocode, checkout::Checkout, coinbase::Coinbase, cybersource::Cybersource,
|
||||
dlocal::Dlocal, fiserv::Fiserv, forte::Forte, globalpay::Globalpay, iatapay::Iatapay,
|
||||
klarna::Klarna, mollie::Mollie, multisafepay::Multisafepay, nexinets::Nexinets, nmi::Nmi,
|
||||
noon::Noon, nuvei::Nuvei, opennode::Opennode, payeezy::Payeezy, paypal::Paypal, payu::Payu,
|
||||
rapyd::Rapyd, shift4::Shift4, stripe::Stripe, trustpay::Trustpay, worldline::Worldline,
|
||||
worldpay::Worldpay, zen::Zen,
|
||||
};
|
||||
|
||||
@ -298,6 +298,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest {
|
||||
api::PaymentMethodData::Crypto(_)
|
||||
| api::PaymentMethodData::BankDebit(_)
|
||||
| api::PaymentMethodData::BankTransfer(_)
|
||||
| api::PaymentMethodData::Reward(_)
|
||||
| api::PaymentMethodData::MandatePayment => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
message: format!("{:?}", item.payment_method),
|
||||
|
||||
@ -139,14 +139,12 @@ fn get_pm_and_subsequent_auth_detail(
|
||||
api::PaymentMethodData::Crypto(_)
|
||||
| api::PaymentMethodData::BankDebit(_)
|
||||
| api::PaymentMethodData::MandatePayment
|
||||
| api::PaymentMethodData::BankTransfer(_) => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
| api::PaymentMethodData::BankTransfer(_)
|
||||
| api::PaymentMethodData::Reward(_) => Err(errors::ConnectorError::NotSupported {
|
||||
message: format!("{:?}", item.request.payment_method_data),
|
||||
connector: "AuthorizeDotNet",
|
||||
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl
|
||||
.to_string(),
|
||||
})?
|
||||
}
|
||||
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),
|
||||
})?,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
461
crates/router/src/connector/cashtocode.rs
Normal file
461
crates/router/src/connector/cashtocode.rs
Normal file
@ -0,0 +1,461 @@
|
||||
mod transformers;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use transformers as cashtocode;
|
||||
|
||||
use crate::{
|
||||
configs::settings,
|
||||
connector::utils as conn_utils,
|
||||
core::errors::{self, CustomResult},
|
||||
db::StorageInterface,
|
||||
headers,
|
||||
services::{
|
||||
self,
|
||||
request::{self, Mask},
|
||||
ConnectorIntegration,
|
||||
},
|
||||
types::{
|
||||
self,
|
||||
api::{self, ConnectorCommon, ConnectorCommonExt},
|
||||
storage::{self},
|
||||
ErrorResponse, Response,
|
||||
},
|
||||
utils::{self, ByteSliceExt, BytesExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cashtocode;
|
||||
|
||||
impl api::Payment for Cashtocode {}
|
||||
impl api::PaymentSession for Cashtocode {}
|
||||
impl api::ConnectorAccessToken for Cashtocode {}
|
||||
impl api::PreVerify for Cashtocode {}
|
||||
impl api::PaymentAuthorize for Cashtocode {}
|
||||
impl api::PaymentSync for Cashtocode {}
|
||||
impl api::PaymentCapture for Cashtocode {}
|
||||
impl api::PaymentVoid for Cashtocode {}
|
||||
impl api::PaymentToken for Cashtocode {}
|
||||
impl api::Refund for Cashtocode {}
|
||||
impl api::RefundExecute for Cashtocode {}
|
||||
impl api::RefundSync for Cashtocode {}
|
||||
|
||||
fn get_auth_cashtocode(
|
||||
payment_method_type: &Option<storage::enums::PaymentMethodType>,
|
||||
auth_type: &types::ConnectorAuthType,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
match payment_method_type
|
||||
.clone()
|
||||
.ok_or_else(conn_utils::missing_field_err("payment_method_type"))
|
||||
{
|
||||
Ok(reward_type) => match reward_type {
|
||||
storage::enums::PaymentMethodType::ClassicReward => match auth_type {
|
||||
types::ConnectorAuthType::BodyKey { api_key, .. } => Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
format!("Basic {api_key}").into_masked(),
|
||||
)]),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
},
|
||||
storage::enums::PaymentMethodType::Evoucher => match auth_type {
|
||||
types::ConnectorAuthType::BodyKey { key1, .. } => Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
format!("Basic {key1}").into_masked(),
|
||||
)]),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
},
|
||||
_ => Err(error_stack::report!(errors::ConnectorError::NotSupported {
|
||||
message: reward_type.to_string(),
|
||||
connector: "cashtocode",
|
||||
payment_experience: "Try with a different payment method".to_string(),
|
||||
})),
|
||||
},
|
||||
Err(_) => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::PaymentMethodToken,
|
||||
types::PaymentMethodTokenizationData,
|
||||
types::PaymentsResponseData,
|
||||
> for Cashtocode
|
||||
{
|
||||
// Not Implemented (R)
|
||||
}
|
||||
|
||||
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Cashtocode where
|
||||
Self: ConnectorIntegration<Flow, Request, Response>
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorCommon for Cashtocode {
|
||||
fn id(&self) -> &'static str {
|
||||
"cashtocode"
|
||||
}
|
||||
|
||||
fn common_get_content_type(&self) -> &'static str {
|
||||
"application/json"
|
||||
}
|
||||
|
||||
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
|
||||
connectors.cashtocode.base_url.as_ref()
|
||||
}
|
||||
|
||||
fn get_auth_header(
|
||||
&self,
|
||||
auth_type: &types::ConnectorAuthType,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let auth = cashtocode::CashtocodeAuthType::try_from(auth_type)
|
||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||
Ok(vec![(
|
||||
headers::AUTHORIZATION.to_string(),
|
||||
auth.api_key.into_masked(),
|
||||
)])
|
||||
}
|
||||
|
||||
fn build_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
let response: cashtocode::CashtocodeErrorResponse = res
|
||||
.response
|
||||
.parse_struct("CashtocodeErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
Ok(ErrorResponse {
|
||||
status_code: res.status_code,
|
||||
code: response.error.to_string(),
|
||||
message: response.error_description,
|
||||
reason: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
//TODO: implement sessions flow
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
|
||||
for Cashtocode
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let mut header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsAuthorizeType::get_content_type(self)
|
||||
.to_owned()
|
||||
.into(),
|
||||
)];
|
||||
let auth_differentiator =
|
||||
get_auth_cashtocode(&req.request.payment_method_type, &req.connector_auth_type);
|
||||
|
||||
let mut api_key = match auth_differentiator {
|
||||
Ok(auth_type) => auth_type,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
header.append(&mut api_key);
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn get_content_type(&self) -> &'static str {
|
||||
self.common_get_content_type()
|
||||
}
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}/merchant/paytokens",
|
||||
connectors.cashtocode.base_url
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let req_obj = cashtocode::CashtocodePaymentsRequest::try_from(req)?;
|
||||
let cashtocode_req = types::RequestBody::log_and_get_request_body(
|
||||
&req_obj,
|
||||
utils::Encode::<cashtocode::CashtocodePaymentsRequest>::encode_to_string_of_json,
|
||||
)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
Ok(Some(cashtocode_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsAuthorizeType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsAuthorizeType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsAuthorizeType::get_request_body(self, req)?)
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsAuthorizeRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: cashtocode::CashtocodePaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("Cashtocode PaymentsAuthorizeResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::PaymentsSyncRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::FlowNotSupported {
|
||||
flow: "Payments Sync".to_string(),
|
||||
connector: "Cashtocode".to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsSyncRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response: cashtocode::CashtocodePaymentsSyncResponse {},
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::RouterData<
|
||||
api::Capture,
|
||||
types::PaymentsCaptureData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::FlowNotSupported {
|
||||
flow: "Capture".to_string(),
|
||||
connector: "Cashtocode".to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::RouterData<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::FlowNotSupported {
|
||||
flow: "Payments Cancel".to_string(),
|
||||
connector: "Cashtocode".to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl api::IncomingWebhook for Cashtocode {
|
||||
fn get_webhook_source_verification_signature(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let base64_signature = conn_utils::get_header_key_value("authorization", request.headers)?;
|
||||
let signature = base64_signature.as_bytes().to_owned();
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
async fn get_webhook_source_verification_merchant_secret(
|
||||
&self,
|
||||
db: &dyn StorageInterface,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<Vec<u8>, errors::ConnectorError> {
|
||||
let key = conn_utils::get_webhook_merchant_secret_key(self.id(), merchant_id);
|
||||
let secret = match db.find_config_by_key(&key).await {
|
||||
Ok(config) => Some(config),
|
||||
Err(e) => {
|
||||
crate::logger::warn!("Unable to fetch merchant webhook secret from DB: {:#?}", e);
|
||||
None
|
||||
}
|
||||
};
|
||||
Ok(secret
|
||||
.map(|conf| conf.config.into_bytes())
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
async fn verify_webhook_source(
|
||||
&self,
|
||||
db: &dyn StorageInterface,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<bool, errors::ConnectorError> {
|
||||
let signature = self
|
||||
.get_webhook_source_verification_signature(request)
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let secret = self
|
||||
.get_webhook_source_verification_merchant_secret(db, merchant_id)
|
||||
.await
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
|
||||
let secret_auth = String::from_utf8(secret.to_vec())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
|
||||
.attach_printable("Could not convert secret to UTF-8")?;
|
||||
let signature_auth = String::from_utf8(signature.to_vec())
|
||||
.into_report()
|
||||
.change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
|
||||
.attach_printable("Could not convert secret to UTF-8")?;
|
||||
Ok(signature_auth == secret_auth)
|
||||
}
|
||||
|
||||
fn get_webhook_object_reference_id(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
|
||||
let webhook: transformers::CashtocodeObjectId = request
|
||||
.body
|
||||
.parse_struct("CashtocodeObjectId")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
|
||||
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
|
||||
api_models::payments::PaymentIdType::ConnectorTransactionId(webhook.transaction_id),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_webhook_event_type(
|
||||
&self,
|
||||
_request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
|
||||
Ok(api::IncomingWebhookEvent::PaymentIntentSuccess)
|
||||
}
|
||||
|
||||
fn get_webhook_resource_object(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
|
||||
let webhook: transformers::CashtocodeIncomingWebhook = request
|
||||
.body
|
||||
.parse_struct("CashtocodeIncomingWebhook")
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
let res_json =
|
||||
utils::Encode::<transformers::CashtocodeIncomingWebhook>::encode_to_value(&webhook)
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
|
||||
Ok(res_json)
|
||||
}
|
||||
|
||||
fn get_webhook_api_response(
|
||||
&self,
|
||||
request: &api::IncomingWebhookRequestDetails<'_>,
|
||||
) -> CustomResult<services::api::ApplicationResponse<serde_json::Value>, errors::ConnectorError>
|
||||
{
|
||||
let status = "EXECUTED".to_string();
|
||||
let obj: transformers::CashtocodeObjectId = request
|
||||
.body
|
||||
.parse_struct("CashtocodeObjectId")
|
||||
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
|
||||
let response: serde_json::Value =
|
||||
serde_json::json!({ "status": status, "transactionId" : obj.transaction_id});
|
||||
Ok(services::api::ApplicationResponse::Json(response))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::refunds::Execute, types::RefundsData, types::RefundsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::RouterData<
|
||||
api::refunds::Execute,
|
||||
types::RefundsData,
|
||||
types::RefundsResponseData,
|
||||
>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::FlowNotSupported {
|
||||
flow: "Refunds".to_string(),
|
||||
connector: "Cashtocode".to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::refunds::RSync, types::RefundsData, types::RefundsResponseData>
|
||||
for Cashtocode
|
||||
{
|
||||
fn build_request(
|
||||
&self,
|
||||
_req: &types::RouterData<
|
||||
api::refunds::RSync,
|
||||
types::RefundsData,
|
||||
types::RefundsResponseData,
|
||||
>,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Err(errors::ConnectorError::FlowNotSupported {
|
||||
flow: "Refund Sync".to_string(),
|
||||
connector: "Cashtocode".to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
226
crates/router/src/connector/cashtocode/transformers.rs
Normal file
226
crates/router/src/connector/cashtocode/transformers.rs
Normal file
@ -0,0 +1,226 @@
|
||||
use common_utils::pii::Email;
|
||||
use masking::Secret;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
connector::utils::RouterData,
|
||||
core::errors,
|
||||
services,
|
||||
types::{self, api, storage::enums},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CashtocodePaymentsRequest {
|
||||
amount: i64,
|
||||
transaction_id: String,
|
||||
user_id: Secret<String>,
|
||||
currency: enums::Currency,
|
||||
first_name: Option<Secret<String>>,
|
||||
last_name: Option<Secret<String>>,
|
||||
user_alias: Secret<String>,
|
||||
requested_url: String,
|
||||
cancel_url: String,
|
||||
email: Option<Email>,
|
||||
mid: String,
|
||||
}
|
||||
|
||||
pub struct CashToCodeMandatoryParams {
|
||||
pub user_id: Secret<String>,
|
||||
pub user_alias: Secret<String>,
|
||||
pub requested_url: String,
|
||||
pub cancel_url: String,
|
||||
}
|
||||
|
||||
fn get_mid(
|
||||
payment_method_data: &api::payments::PaymentMethodData,
|
||||
) -> Result<String, errors::ConnectorError> {
|
||||
match payment_method_data {
|
||||
api_models::payments::PaymentMethodData::Reward(reward_data) => {
|
||||
Ok(reward_data.merchant_id.to_string())
|
||||
}
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
"Payment methods".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mandatory_params(
|
||||
item: &types::PaymentsAuthorizeRouterData,
|
||||
) -> Result<CashToCodeMandatoryParams, error_stack::Report<errors::ConnectorError>> {
|
||||
let customer_id = item.get_customer_id()?;
|
||||
let url = item.get_return_url()?;
|
||||
Ok(CashToCodeMandatoryParams {
|
||||
user_id: Secret::new(customer_id.to_owned()),
|
||||
user_alias: Secret::new(customer_id),
|
||||
requested_url: url.to_owned(),
|
||||
cancel_url: url,
|
||||
})
|
||||
}
|
||||
|
||||
impl TryFrom<&types::PaymentsAuthorizeRouterData> for CashtocodePaymentsRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
|
||||
let params: CashToCodeMandatoryParams = get_mandatory_params(item)?;
|
||||
let mid = get_mid(&item.request.payment_method_data)?;
|
||||
match item.payment_method {
|
||||
storage_models::enums::PaymentMethod::Reward => Ok(Self {
|
||||
amount: item.request.amount,
|
||||
transaction_id: item.attempt_id.clone(),
|
||||
currency: item.request.currency,
|
||||
user_id: params.user_id,
|
||||
first_name: None,
|
||||
last_name: None,
|
||||
user_alias: params.user_alias,
|
||||
requested_url: params.requested_url,
|
||||
cancel_url: params.cancel_url,
|
||||
email: item.request.email.clone(),
|
||||
mid,
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CashtocodeAuthType {
|
||||
pub(super) api_key: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&types::ConnectorAuthType> for CashtocodeAuthType {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
|
||||
match auth_type {
|
||||
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
|
||||
api_key: api_key.to_string(),
|
||||
}),
|
||||
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum CashtocodePaymentStatus {
|
||||
Succeeded,
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
|
||||
impl From<CashtocodePaymentStatus> for enums::AttemptStatus {
|
||||
fn from(item: CashtocodePaymentStatus) -> Self {
|
||||
match item {
|
||||
CashtocodePaymentStatus::Succeeded => Self::Charged,
|
||||
CashtocodePaymentStatus::Processing => Self::AuthenticationPending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct CashtocodeErrors {
|
||||
pub message: String,
|
||||
pub path: String,
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CashtocodePaymentsResponse {
|
||||
pub pay_url: String,
|
||||
}
|
||||
|
||||
pub struct CashtocodePaymentsSyncResponse {}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<F, CashtocodePaymentsResponse, T, types::PaymentsResponseData>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CashtocodePaymentsResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let redirection_data = services::RedirectForm::Form {
|
||||
endpoint: item.response.pay_url.clone(),
|
||||
method: services::Method::Post,
|
||||
form_fields: Default::default(),
|
||||
};
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::AuthenticationPending,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.data.attempt_id.clone(),
|
||||
),
|
||||
redirection_data: Some(redirection_data),
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
CashtocodePaymentsSyncResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, T, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
CashtocodePaymentsSyncResponse,
|
||||
T,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
status: enums::AttemptStatus::Charged,
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
resource_id: types::ResponseId::ConnectorTransactionId(
|
||||
item.data.attempt_id.clone(),
|
||||
),
|
||||
redirection_data: None,
|
||||
mandate_reference: None,
|
||||
connector_metadata: None,
|
||||
network_txn_id: None,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CashtocodeErrorResponse {
|
||||
pub error: String,
|
||||
pub error_description: String,
|
||||
pub errors: Option<Vec<CashtocodeErrors>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CashtocodeIncomingWebhook {
|
||||
pub amount: i64,
|
||||
pub currency: String,
|
||||
pub foreign_transaction_id: String,
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: String,
|
||||
pub transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CashtocodeObjectId {
|
||||
pub transaction_id: String,
|
||||
}
|
||||
@ -2566,14 +2566,13 @@ impl
|
||||
)),
|
||||
}
|
||||
}
|
||||
api::PaymentMethodData::MandatePayment | api::PaymentMethodData::Crypto(_) => {
|
||||
Err(errors::ConnectorError::NotSupported {
|
||||
api::PaymentMethodData::MandatePayment
|
||||
| api::PaymentMethodData::Crypto(_)
|
||||
| api::PaymentMethodData::Reward(_) => Err(errors::ConnectorError::NotSupported {
|
||||
message: format!("{pm_type:?}"),
|
||||
connector: "Stripe",
|
||||
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl
|
||||
.to_string(),
|
||||
})?
|
||||
}
|
||||
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),
|
||||
})?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,6 +142,7 @@ default_imp_for_complete_authorize!(
|
||||
connector::Authorizedotnet,
|
||||
connector::Bitpay,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Checkout,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
@ -200,6 +201,7 @@ default_imp_for_create_customer!(
|
||||
connector::Bambora,
|
||||
connector::Bitpay,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Checkout,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
@ -262,6 +264,7 @@ default_imp_for_connector_redirect_response!(
|
||||
connector::Authorizedotnet,
|
||||
connector::Bitpay,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
connector::Dlocal,
|
||||
@ -301,6 +304,7 @@ default_imp_for_connector_request_id!(
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Checkout,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
@ -366,6 +370,7 @@ default_imp_for_accept_dispute!(
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
connector::Dlocal,
|
||||
@ -451,6 +456,7 @@ default_imp_for_file_upload!(
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
connector::Dlocal,
|
||||
@ -513,6 +519,7 @@ default_imp_for_submit_evidence!(
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Cybersource,
|
||||
connector::Coinbase,
|
||||
connector::Dlocal,
|
||||
@ -575,6 +582,7 @@ default_imp_for_defend_dispute!(
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Cybersource,
|
||||
connector::Coinbase,
|
||||
connector::Dlocal,
|
||||
@ -638,6 +646,7 @@ default_imp_for_pre_processing_steps!(
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Braintree,
|
||||
connector::Cashtocode,
|
||||
connector::Checkout,
|
||||
connector::Coinbase,
|
||||
connector::Cybersource,
|
||||
|
||||
@ -1271,6 +1271,7 @@ pub async fn make_pm_data<'a, F: Clone, R>(
|
||||
(pm @ Some(api::PaymentMethodData::BankRedirect(_)), _) => Ok(pm.to_owned()),
|
||||
(pm @ Some(api::PaymentMethodData::Crypto(_)), _) => Ok(pm.to_owned()),
|
||||
(pm @ Some(api::PaymentMethodData::BankDebit(_)), _) => Ok(pm.to_owned()),
|
||||
(pm @ Some(api::PaymentMethodData::Reward(_)), _) => Ok(pm.to_owned()),
|
||||
(pm_opt @ Some(pm @ api::PaymentMethodData::BankTransfer(_)), _) => {
|
||||
let token = vault::Vault::store_payment_method_data_in_locker(
|
||||
state,
|
||||
|
||||
@ -40,7 +40,6 @@ pub async fn payments_incoming_webhook_flow<W: api::OutgoingWebhookType>(
|
||||
} else {
|
||||
payments::CallConnectorAction::Trigger
|
||||
};
|
||||
|
||||
let payments_response = match webhook_details.object_reference_id {
|
||||
api_models::webhooks::ObjectReferenceId::PaymentId(id) => {
|
||||
payments::payments_core::<api::PSync, api::PaymentsResponse, _, _, _>(
|
||||
|
||||
@ -169,6 +169,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::payments::WeChatPayRedirection,
|
||||
api_models::payments::BankDebitBilling,
|
||||
api_models::payments::CryptoData,
|
||||
api_models::payments::RewardData,
|
||||
api_models::payments::Address,
|
||||
api_models::payments::BankRedirectData,
|
||||
api_models::payments::BankRedirectBilling,
|
||||
|
||||
@ -410,8 +410,9 @@ impl Webhooks {
|
||||
.route(
|
||||
web::post().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>),
|
||||
)
|
||||
.route(web::get().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>))
|
||||
.route(
|
||||
web::get().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>),
|
||||
web::put().to(receive_incoming_webhook::<webhook_type::OutgoingWebhook>),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -209,6 +209,7 @@ impl ConnectorData {
|
||||
enums::Connector::Bitpay => Ok(Box::new(&connector::Bitpay)),
|
||||
enums::Connector::Bluesnap => Ok(Box::new(&connector::Bluesnap)),
|
||||
enums::Connector::Braintree => Ok(Box::new(&connector::Braintree)),
|
||||
enums::Connector::Cashtocode => Ok(Box::new(&connector::Cashtocode)),
|
||||
enums::Connector::Checkout => Ok(Box::new(&connector::Checkout)),
|
||||
enums::Connector::Coinbase => Ok(Box::new(&connector::Coinbase)),
|
||||
enums::Connector::Cybersource => Ok(Box::new(&connector::Cybersource)),
|
||||
|
||||
127
crates/router/tests/connectors/cashtocode.rs
Normal file
127
crates/router/tests/connectors/cashtocode.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use api_models::payments::{Address, AddressDetails};
|
||||
use router::types::{self, storage::enums};
|
||||
|
||||
use crate::{
|
||||
connector_auth,
|
||||
utils::{self, ConnectorActions},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct CashtocodeTest;
|
||||
impl ConnectorActions for CashtocodeTest {}
|
||||
impl utils::Connector for CashtocodeTest {
|
||||
fn get_data(&self) -> types::api::ConnectorData {
|
||||
use router::connector::Cashtocode;
|
||||
types::api::ConnectorData {
|
||||
connector: Box::new(&Cashtocode),
|
||||
connector_name: types::Connector::Cashtocode,
|
||||
get_token: types::api::GetToken::Connector,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_auth_token(&self) -> types::ConnectorAuthType {
|
||||
types::ConnectorAuthType::from(
|
||||
connector_auth::ConnectorAuthentication::new()
|
||||
.cashtocode
|
||||
.expect("Missing connector authentication configuration"),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_name(&self) -> String {
|
||||
"cashtocode".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
static CONNECTOR: CashtocodeTest = CashtocodeTest {};
|
||||
|
||||
impl CashtocodeTest {
|
||||
fn get_payment_authorize_data(
|
||||
payment_method_type: Option<enums::PaymentMethodType>,
|
||||
payment_method_data: types::api::PaymentMethodData,
|
||||
) -> Option<types::PaymentsAuthorizeData> {
|
||||
Some(types::PaymentsAuthorizeData {
|
||||
amount: 3500,
|
||||
currency: enums::Currency::USD,
|
||||
payment_method_data,
|
||||
confirm: true,
|
||||
statement_descriptor_suffix: None,
|
||||
statement_descriptor: None,
|
||||
setup_future_usage: None,
|
||||
mandate_id: None,
|
||||
off_session: None,
|
||||
setup_mandate_details: None,
|
||||
capture_method: None,
|
||||
browser_info: None,
|
||||
order_details: None,
|
||||
order_category: None,
|
||||
email: None,
|
||||
payment_experience: None,
|
||||
payment_method_type,
|
||||
session_token: None,
|
||||
enrolled_for_3ds: false,
|
||||
related_transaction_id: None,
|
||||
router_return_url: Some(String::from("http://localhost:8080")),
|
||||
webhook_url: None,
|
||||
complete_authorize_url: None,
|
||||
customer_id: Some("John Doe".to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_payment_info() -> Option<utils::PaymentInfo> {
|
||||
Some(utils::PaymentInfo {
|
||||
address: Some(types::PaymentAddress {
|
||||
billing: Some(Address {
|
||||
address: Some(AddressDetails {
|
||||
country: Some(api_models::enums::CountryAlpha2::US),
|
||||
..Default::default()
|
||||
}),
|
||||
phone: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
return_url: Some("https://google.com".to_owned()),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//fetch payurl for payment's create
|
||||
#[actix_web::test]
|
||||
async fn should_fetch_pay_url_classic() {
|
||||
let authorize_response = CONNECTOR
|
||||
.make_payment(
|
||||
CashtocodeTest::get_payment_authorize_data(
|
||||
Some(enums::PaymentMethodType::ClassicReward),
|
||||
api_models::payments::PaymentMethodData::Reward(api_models::payments::RewardData {
|
||||
merchant_id: "1bc20b0a".to_owned(),
|
||||
}),
|
||||
),
|
||||
CashtocodeTest::get_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
authorize_response.status,
|
||||
enums::AttemptStatus::AuthenticationPending
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_web::test]
|
||||
async fn should_fetch_pay_url_evoucher() {
|
||||
let authorize_response = CONNECTOR
|
||||
.make_payment(
|
||||
CashtocodeTest::get_payment_authorize_data(
|
||||
Some(enums::PaymentMethodType::Evoucher),
|
||||
api_models::payments::PaymentMethodData::Reward(api_models::payments::RewardData {
|
||||
merchant_id: "befb46ee".to_owned(),
|
||||
}),
|
||||
),
|
||||
CashtocodeTest::get_payment_info(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
authorize_response.status,
|
||||
enums::AttemptStatus::AuthenticationPending
|
||||
);
|
||||
}
|
||||
@ -13,6 +13,7 @@ pub struct ConnectorAuthentication {
|
||||
pub bambora: Option<BodyKey>,
|
||||
pub bitpay: Option<HeaderKey>,
|
||||
pub bluesnap: Option<BodyKey>,
|
||||
pub cashtocode: Option<BodyKey>,
|
||||
pub checkout: Option<SignatureKey>,
|
||||
pub coinbase: Option<HeaderKey>,
|
||||
pub cybersource: Option<SignatureKey>,
|
||||
@ -48,7 +49,6 @@ pub struct ConnectorAuthentication {
|
||||
impl ConnectorAuthentication {
|
||||
#[allow(clippy::expect_used)]
|
||||
pub(crate) fn new() -> Self {
|
||||
// Do `export CONNECTOR_AUTH_FILE_PATH="/hyperswitch/crates/router/tests/connectors/sample_auth.toml"`
|
||||
// before running tests
|
||||
let path = env::var("CONNECTOR_AUTH_FILE_PATH")
|
||||
.expect("connector authentication file path not set");
|
||||
|
||||
@ -13,6 +13,7 @@ mod authorizedotnet;
|
||||
mod bambora;
|
||||
mod bitpay;
|
||||
mod bluesnap;
|
||||
mod cashtocode;
|
||||
mod checkout;
|
||||
mod coinbase;
|
||||
mod connector_auth;
|
||||
|
||||
@ -126,3 +126,7 @@ pypl_email=""
|
||||
pypl_pass=""
|
||||
gmail_email=""
|
||||
gmail_pass=""
|
||||
|
||||
[cashtocode]
|
||||
api_key="Classic PMT API Key"
|
||||
key1 = "Evoucher PMT API Key"
|
||||
@ -464,6 +464,7 @@ pub enum PaymentMethod {
|
||||
BankTransfer,
|
||||
Crypto,
|
||||
BankDebit,
|
||||
Reward,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@ -679,10 +680,13 @@ pub enum PaymentMethodType {
|
||||
BancontactCard,
|
||||
Becs,
|
||||
Blik,
|
||||
#[serde(rename = "classic")]
|
||||
ClassicReward,
|
||||
Credit,
|
||||
CryptoCurrency,
|
||||
Debit,
|
||||
Eps,
|
||||
Evoucher,
|
||||
Giropay,
|
||||
GooglePay,
|
||||
Ideal,
|
||||
|
||||
@ -66,6 +66,7 @@ bambora.base_url = "https://api.na.bambora.com"
|
||||
bitpay.base_url = "https://test.bitpay.com"
|
||||
bluesnap.base_url = "https://sandbox.bluesnap.com/"
|
||||
braintree.base_url = "https://api.sandbox.braintreegateway.com/"
|
||||
cashtocode.base_url = "https://cluster05.api-test.cashtocode.com"
|
||||
checkout.base_url = "https://api.sandbox.checkout.com/"
|
||||
coinbase.base_url = "https://api.commerce.coinbase.com"
|
||||
cybersource.base_url = "https://apitest.cybersource.com/"
|
||||
@ -98,6 +99,7 @@ zen.base_url = "https://api.zen-test.com/"
|
||||
|
||||
[connectors.supported]
|
||||
wallets = ["klarna", "braintree", "applepay"]
|
||||
rewards = ["cashtocode",]
|
||||
cards = [
|
||||
"aci",
|
||||
"adyen",
|
||||
|
||||
@ -4,7 +4,7 @@ function find_prev_connector() {
|
||||
git checkout $self
|
||||
cp $self $self.tmp
|
||||
# add new connector to existing list and sort it
|
||||
connectors=(aci adyen airwallex applepay authorizedotnet bambora bitpay bluesnap braintree checkout coinbase cybersource dlocal dummyconnector fiserv forte globalpay iatapay klarna mollie multisafepay nexinets noon nuvei opennode payeezy paypal payu rapyd shift4 stripe trustpay worldline worldpay "$1")
|
||||
connectors=(aci adyen airwallex applepay authorizedotnet bambora bitpay bluesnap braintree cashtocode checkout coinbase cybersource dlocal dummyconnector fiserv forte globalpay iatapay klarna mollie multisafepay nexinets noon nuvei opennode payeezy paypal payu rapyd shift4 stripe trustpay worldline worldpay "$1")
|
||||
IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS
|
||||
res=`echo ${sorted[@]}`
|
||||
sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp
|
||||
|
||||
Reference in New Issue
Block a user