Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
108 changes: 98 additions & 10 deletions crates/hyperswitch_connectors/src/connectors/peachpayments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use std::sync::LazyLock;
use common_enums::{self, enums};
use common_utils::{
errors::CustomResult,
ext_traits::BytesExt,
ext_traits::{ByteSliceExt, BytesExt},
id_type,
request::{Method, Request, RequestBuilder, RequestContent},
types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
router_flow_types::{
Expand Down Expand Up @@ -42,7 +43,7 @@ use hyperswitch_interfaces::{
types::{self, Response},
webhooks,
};
use masking::{ExposeInterface, Mask};
use masking::{ExposeInterface, Mask, Secret};
use transformers as peachpayments;

use crate::{constants::headers, types::ResponseRouterData, utils};
Expand Down Expand Up @@ -542,23 +543,109 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Peachpaym
impl webhooks::IncomingWebhook for Peachpayments {
fn get_webhook_object_reference_id(
&self,
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
request: &webhooks::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
let webhook_body: peachpayments::PeachpaymentsIncomingWebhook = request
.body
.parse_struct("PeachpaymentsIncomingWebhook")
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

let reference_id = webhook_body
.transaction
.as_ref()
.map(|txn| txn.reference_id.clone())
.ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?;

Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::PaymentAttemptId(reference_id),
))
}

fn get_webhook_event_type(
&self,
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
request: &webhooks::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
let webhook_body: peachpayments::PeachpaymentsIncomingWebhook = request
.body
.parse_struct("PeachpaymentsIncomingWebhook")
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

match webhook_body.webhook_type.as_str() {
"transaction" => {
if let Some(transaction) = webhook_body.transaction {
match transaction.transaction_result {
peachpayments::PeachpaymentsPaymentStatus::Successful
| peachpayments::PeachpaymentsPaymentStatus::ApprovedConfirmed => {
Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
}
peachpayments::PeachpaymentsPaymentStatus::Pending
| peachpayments::PeachpaymentsPaymentStatus::Authorized
| peachpayments::PeachpaymentsPaymentStatus::Approved => {
Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
}
peachpayments::PeachpaymentsPaymentStatus::Declined
| peachpayments::PeachpaymentsPaymentStatus::Failed => {
Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentFailure)
}
peachpayments::PeachpaymentsPaymentStatus::Voided
| peachpayments::PeachpaymentsPaymentStatus::Reversed => {
Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentCancelled)
}
peachpayments::PeachpaymentsPaymentStatus::ThreedsRequired => {
Ok(api_models::webhooks::IncomingWebhookEvent::PaymentActionRequired)
}
}
} else {
Err(errors::ConnectorError::WebhookEventTypeNotFound)
}
}
_ => Err(errors::ConnectorError::WebhookEventTypeNotFound),
}
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)
}

fn get_webhook_resource_object(
&self,
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
request: &webhooks::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
Err(report!(errors::ConnectorError::WebhooksNotImplemented))
let webhook_body: peachpayments::PeachpaymentsIncomingWebhook = request
.body
.parse_struct("PeachpaymentsIncomingWebhook")
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

let transaction = webhook_body
.transaction
.ok_or(errors::ConnectorError::WebhookResourceObjectNotFound)?;

let payments_response = peachpayments::PeachpaymentsPaymentsResponse {
transaction_id: transaction.transaction_id,
response_code: transaction.response_code,
transaction_result: transaction.transaction_result,
ecommerce_card_payment_only_transaction_data: transaction
.ecommerce_card_payment_only_transaction_data
.map(|data| peachpayments::EcommerceCardPaymentOnlyResponseData {
amount: data.amount,
stan: data.stan,
rrn: data.rrn,
approval_code: data.approval_code,
merchant_advice_code: data.merchant_advice_code,
description: None,
trace_id: data.trace_id,
}),
};

Ok(Box::new(payments_response))
}

async fn verify_webhook_source(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have information on source_verification_algorithm for this connector?
If so, can we add implementations for get_webhook_source_verification_algorithm, get_webhook_source_verification_signature, and get_webhook_source_verification_message as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peach doesn't support any algorithm as of now.

&self,
_request: &webhooks::IncomingWebhookRequestDetails<'_>,
_merchant_id: &id_type::MerchantId,
_connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
_connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>,
_connector_name: &str,
) -> CustomResult<bool, errors::ConnectorError> {
Ok(false)
}
}

Expand Down Expand Up @@ -625,7 +712,8 @@ static PEACHPAYMENTS_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
integration_status: enums::ConnectorIntegrationStatus::Beta,
};

static PEACHPAYMENTS_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];
static PEACHPAYMENTS_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 1] =
[enums::EventClass::Payments];

impl ConnectorSpecifications for Peachpayments {
fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub struct EcommerceCardPaymentOnlyTransactionData {
pub routing: Routing,
pub card: CardDetails,
pub amount: AmountDetails,
pub rrn: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
Expand Down Expand Up @@ -375,6 +376,7 @@ impl TryFrom<&PeachpaymentsRouterData<&PaymentsAuthorizeRouterData>>
routing,
card,
amount,
rrn: item.router_data.request.merchant_order_reference_id.clone(),
};

// Generate current timestamp for sendDateTime (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ)
Expand Down Expand Up @@ -682,6 +684,28 @@ impl TryFrom<&PeachpaymentsRouterData<&PaymentsAuthorizeRouterData>>
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PeachpaymentsIncomingWebhook {
pub webhook_id: String,
pub webhook_type: String,
pub reversal_failure_reason: Option<String>,
pub transaction: Option<WebhookTransaction>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WebhookTransaction {
pub transaction_id: String,
pub original_transaction_id: Option<String>,
pub reference_id: String,
pub transaction_result: PeachpaymentsPaymentStatus,
pub error_message: Option<String>,
pub response_code: Option<ResponseCode>,
pub ecommerce_card_payment_only_transaction_data: Option<EcommerceCardPaymentOnlyResponseData>,
pub payment_method: String,
}

// Error Response
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down
Loading