feat(connector): [bitpay] Add new crypto connector bitpay & testcases for all crypto connectors (#919)

Co-authored-by: arvindpatel24 <arvind.patel@juspay.in>
This commit is contained in:
Arvind Patel
2023-05-11 16:36:47 +05:30
committed by GitHub
parent dfc6be4e4f
commit f70f10aac5
20 changed files with 1170 additions and 717 deletions

View File

@ -359,6 +359,7 @@ pub struct Connectors {
pub applepay: ConnectorParams,
pub authorizedotnet: ConnectorParams,
pub bambora: ConnectorParams,
pub bitpay: ConnectorParams,
pub bluesnap: ConnectorParams,
pub braintree: ConnectorParams,
pub checkout: ConnectorParams,

View File

@ -3,6 +3,7 @@ pub mod adyen;
pub mod airwallex;
pub mod authorizedotnet;
pub mod bambora;
pub mod bitpay;
pub mod bluesnap;
pub mod braintree;
pub mod checkout;
@ -38,7 +39,7 @@ pub mod mollie;
pub use self::dummyconnector::DummyConnector;
pub use self::{
aci::Aci, adyen::Adyen, airwallex::Airwallex, authorizedotnet::Authorizedotnet,
bambora::Bambora, bluesnap::Bluesnap, braintree::Braintree, checkout::Checkout,
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, nuvei::Nuvei, opennode::Opennode,

View File

@ -0,0 +1,533 @@
mod transformers;
use std::fmt::Debug;
use common_utils::{errors::ReportSwitchExt, ext_traits::ByteSliceExt};
use error_stack::ResultExt;
use transformers as bitpay;
use self::bitpay::BitpayWebhookDetails;
use crate::{
configs::settings,
core::errors::{self, CustomResult},
headers,
services::{self, ConnectorIntegration},
types::{
self,
api::{self, ConnectorCommon, ConnectorCommonExt},
ErrorResponse, Response,
},
utils::{self, BytesExt, Encode},
};
#[derive(Debug, Clone)]
pub struct Bitpay;
impl api::Payment for Bitpay {}
impl api::PaymentToken for Bitpay {}
impl api::PaymentSession for Bitpay {}
impl api::ConnectorAccessToken for Bitpay {}
impl api::PreVerify for Bitpay {}
impl api::PaymentAuthorize for Bitpay {}
impl api::PaymentSync for Bitpay {}
impl api::PaymentCapture for Bitpay {}
impl api::PaymentVoid for Bitpay {}
impl api::Refund for Bitpay {}
impl api::RefundExecute for Bitpay {}
impl api::RefundSync for Bitpay {}
impl
ConnectorIntegration<
api::PaymentMethodToken,
types::PaymentMethodTokenizationData,
types::PaymentsResponseData,
> for Bitpay
{
// Not Implemented (R)
}
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Bitpay
where
Self: ConnectorIntegration<Flow, Request, Response>,
{
fn build_headers(
&self,
_req: &types::RouterData<Flow, Request, Response>,
_connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
let header = vec![
(
headers::CONTENT_TYPE.to_string(),
types::PaymentsAuthorizeType::get_content_type(self).to_string(),
),
(headers::X_ACCEPT_VERSION.to_string(), "2.0.0".to_string()),
];
Ok(header)
}
}
impl ConnectorCommon for Bitpay {
fn id(&self) -> &'static str {
"bitpay"
}
fn common_get_content_type(&self) -> &'static str {
"application/json"
}
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
connectors.bitpay.base_url.as_ref()
}
fn get_auth_header(
&self,
auth_type: &types::ConnectorAuthType,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
let auth = bitpay::BitpayAuthType::try_from(auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
Ok(vec![(headers::AUTHORIZATION.to_string(), auth.api_key)])
}
fn build_error_response(
&self,
res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
let response: bitpay::BitpayErrorResponse =
res.response.parse_struct("BitpayErrorResponse").switch()?;
Ok(ErrorResponse {
status_code: res.status_code,
code: response.code,
message: response.message,
reason: response.reason,
})
}
}
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
for Bitpay
{
//TODO: implement sessions flow
}
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
for Bitpay
{
}
impl ConnectorIntegration<api::Verify, types::VerifyRequestData, types::PaymentsResponseData>
for Bitpay
{
}
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
for Bitpay
{
fn get_headers(
&self,
req: &types::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
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!("{}/invoices", self.base_url(connectors)))
}
fn get_request_body(
&self,
req: &types::PaymentsAuthorizeRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> {
let req_obj = bitpay::BitpayPaymentsRequest::try_from(req)?;
let bitpay_req =
utils::Encode::<bitpay::BitpayPaymentsRequest>::encode_to_string_of_json(&req_obj)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(bitpay_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: bitpay::BitpayPaymentsResponse = res
.response
.parse_struct("Bitpay PaymentsAuthorizeResponse")
.switch()?;
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 Bitpay
{
fn get_headers(
&self,
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
let auth = bitpay::BitpayAuthType::try_from(&req.connector_auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
let connector_id = req
.request
.connector_transaction_id
.get_connector_transaction_id()
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
Ok(format!(
"{}/invoices/{}?token={}",
self.base_url(connectors),
connector_id,
auth.api_key
))
}
fn build_request(
&self,
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Get)
.url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::PaymentsSyncRouterData,
res: Response,
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
let response: bitpay::BitpayPaymentsResponse = res
.response
.parse_struct("bitpay PaymentsSyncResponse")
.switch()?;
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::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
for Bitpay
{
fn get_headers(
&self,
req: &types::PaymentsCaptureRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
_req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn get_request_body(
&self,
_req: &types::PaymentsCaptureRouterData,
) -> CustomResult<Option<String>, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
}
fn build_request(
&self,
req: &types::PaymentsCaptureRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::PaymentsCaptureType::get_headers(
self, req, connectors,
)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::PaymentsCaptureRouterData,
res: Response,
) -> CustomResult<types::PaymentsCaptureRouterData, errors::ConnectorError> {
let response: bitpay::BitpayPaymentsResponse = res
.response
.parse_struct("Bitpay PaymentsCaptureResponse")
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
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::Void, types::PaymentsCancelData, types::PaymentsResponseData>
for Bitpay
{
}
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Bitpay {
fn get_headers(
&self,
req: &types::RefundsRouterData<api::Execute>,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
_req: &types::RefundsRouterData<api::Execute>,
_connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn get_request_body(
&self,
req: &types::RefundsRouterData<api::Execute>,
) -> CustomResult<Option<String>, errors::ConnectorError> {
let req_obj = bitpay::BitpayRefundRequest::try_from(req)?;
let bitpay_req =
utils::Encode::<bitpay::BitpayRefundRequest>::encode_to_string_of_json(&req_obj)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(bitpay_req))
}
fn build_request(
&self,
req: &types::RefundsRouterData<api::Execute>,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
let request = services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::RefundExecuteType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::RefundExecuteType::get_headers(
self, req, connectors,
)?)
.body(types::RefundExecuteType::get_request_body(self, req)?)
.build();
Ok(Some(request))
}
fn handle_response(
&self,
data: &types::RefundsRouterData<api::Execute>,
res: Response,
) -> CustomResult<types::RefundsRouterData<api::Execute>, errors::ConnectorError> {
let response: bitpay::RefundResponse =
res.response
.parse_struct("bitpay RefundResponse")
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
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::RSync, types::RefundsData, types::RefundsResponseData> for Bitpay {
fn get_headers(
&self,
req: &types::RefundSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
_req: &types::RefundSyncRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn build_request(
&self,
req: &types::RefundSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Get)
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
.body(types::RefundSyncType::get_request_body(self, req)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::RefundSyncRouterData,
res: Response,
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
let response: bitpay::RefundResponse = res
.response
.parse_struct("bitpay RefundSyncResponse")
.switch()?;
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)
}
}
#[async_trait::async_trait]
impl api::IncomingWebhook for Bitpay {
fn get_webhook_object_reference_id(
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::webhooks::ObjectReferenceId, errors::ConnectorError> {
let notif: BitpayWebhookDetails = request
.body
.parse_struct("BitpayWebhookDetails")
.change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
api_models::payments::PaymentIdType::ConnectorTransactionId(notif.data.id),
))
}
fn get_webhook_event_type(
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
let notif: BitpayWebhookDetails = request
.body
.parse_struct("BitpayWebhookDetails")
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
match notif.event.name {
bitpay::WebhookEventType::Confirmed | bitpay::WebhookEventType::Completed => {
Ok(api::IncomingWebhookEvent::PaymentIntentSuccess)
}
bitpay::WebhookEventType::Paid => {
Ok(api::IncomingWebhookEvent::PaymentIntentProcessing)
}
bitpay::WebhookEventType::Declined => {
Ok(api::IncomingWebhookEvent::PaymentIntentFailure)
}
_ => Ok(api::IncomingWebhookEvent::EventNotSupported),
}
}
fn get_webhook_resource_object(
&self,
request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<serde_json::Value, errors::ConnectorError> {
let notif: BitpayWebhookDetails = request
.body
.parse_struct("BitpayWebhookDetails")
.change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
Encode::<BitpayWebhookDetails>::encode_to_value(&notif)
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)
}
}

View File

@ -0,0 +1,287 @@
use reqwest::Url;
use serde::{Deserialize, Serialize};
use crate::{
connector::utils::PaymentsAuthorizeRequestData,
core::errors,
services,
types::{self, api, storage::enums, ConnectorAuthType},
};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum TransactionSpeed {
Low,
#[default]
Medium,
High,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BitpayPaymentsRequest {
price: i64,
currency: String,
#[serde(rename = "redirectURL")]
redirect_url: String,
#[serde(rename = "notificationURL")]
notification_url: String,
transaction_speed: TransactionSpeed,
token: String,
}
impl TryFrom<&types::PaymentsAuthorizeRouterData> for BitpayPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
get_crypto_specific_payment_data(item)
}
}
// Auth Struct
pub struct BitpayAuthType {
pub(super) api_key: String,
}
impl TryFrom<&ConnectorAuthType> for BitpayAuthType {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &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()),
}
}
}
// PaymentsResponse
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum BitpayPaymentStatus {
#[default]
New,
Paid,
Confirmed,
Complete,
Expired,
Invalid,
}
impl From<BitpayPaymentStatus> for enums::AttemptStatus {
fn from(item: BitpayPaymentStatus) -> Self {
match item {
BitpayPaymentStatus::New => Self::AuthenticationPending,
BitpayPaymentStatus::Complete | BitpayPaymentStatus::Confirmed => Self::Charged,
BitpayPaymentStatus::Expired => Self::Failure,
_ => Self::Pending,
}
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ExceptionStatus {
#[default]
Unit,
Bool(bool),
String(String),
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BitpayPaymentResponseData {
pub url: Option<Url>,
pub status: BitpayPaymentStatus,
pub price: i64,
pub currency: String,
pub amount_paid: i64,
pub invoice_time: Option<i64>,
pub rate_refresh_time: Option<i64>,
pub expiration_time: Option<i64>,
pub current_time: Option<i64>,
pub id: String,
pub low_fee_detected: Option<bool>,
pub display_amount_paid: Option<String>,
pub exception_status: ExceptionStatus,
pub redirect_url: Option<String>,
pub refund_address_request_pending: Option<bool>,
pub merchant_name: Option<String>,
pub token: Option<String>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct BitpayPaymentsResponse {
data: BitpayPaymentResponseData,
facade: Option<String>,
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, BitpayPaymentsResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, BitpayPaymentsResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data = item
.response
.data
.url
.map(|x| services::RedirectForm::from((x, services::Method::Get)));
let connector_id = types::ResponseId::ConnectorTransactionId(item.response.data.id);
let attempt_status = item.response.data.status;
Ok(Self {
status: enums::AttemptStatus::from(attempt_status),
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: connector_id,
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
}),
..item.data
})
}
}
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct BitpayRefundRequest {
pub amount: i64,
}
impl<F> TryFrom<&types::RefundsRouterData<F>> for BitpayRefundRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::RefundsRouterData<F>) -> Result<Self, Self::Error> {
Ok(Self {
amount: item.request.amount,
})
}
}
// Type definition for Refund Response
#[allow(dead_code)]
#[derive(Debug, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
Succeeded,
Failed,
#[default]
Processing,
}
impl From<RefundStatus> for enums::RefundStatus {
fn from(item: RefundStatus) -> Self {
match item {
RefundStatus::Succeeded => Self::Success,
RefundStatus::Failed => Self::Failure,
RefundStatus::Processing => Self::Pending,
//TODO: Review mapping
}
}
}
//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
id: String,
status: RefundStatus,
}
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
for types::RefundsRouterData<api::Execute>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status),
}),
..item.data
})
}
}
impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
for types::RefundsRouterData<api::RSync>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::RefundsResponseRouterData<api::RSync, RefundResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status),
}),
..item.data
})
}
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct BitpayErrorResponse {
pub status_code: u16,
pub code: String,
pub message: String,
pub reason: Option<String>,
}
fn get_crypto_specific_payment_data(
item: &types::PaymentsAuthorizeRouterData,
) -> Result<BitpayPaymentsRequest, error_stack::Report<errors::ConnectorError>> {
let price = item.request.amount;
let currency = item.request.currency.to_string();
let redirect_url = item.request.get_return_url()?;
let notification_url = item.request.get_webhook_url()?;
let transaction_speed = TransactionSpeed::Medium;
let auth_type = item.connector_auth_type.clone();
let token = match auth_type {
ConnectorAuthType::HeaderKey { api_key } => api_key,
_ => String::default(),
};
Ok(BitpayPaymentsRequest {
price,
currency,
redirect_url,
notification_url,
transaction_speed,
token,
})
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BitpayWebhookDetails {
pub event: Event,
pub data: BitpayPaymentResponseData,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Event {
pub code: i64,
pub name: WebhookEventType,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum WebhookEventType {
#[serde(rename = "invoice_paidInFull")]
Paid,
#[serde(rename = "invoice_confirmed")]
Confirmed,
#[serde(rename = "invoice_completed")]
Completed,
#[serde(rename = "invoice_expired")]
Expired,
#[serde(rename = "invoice_failedToConfirm")]
Invalid,
#[serde(rename = "invoice_declined")]
Declined,
#[serde(rename = "invoice_refundComplete")]
Refunded,
#[serde(rename = "invoice_manuallyNotified")]
Resent,
}

View File

@ -107,6 +107,7 @@ default_imp_for_complete_authorize!(
connector::Aci,
connector::Adyen,
connector::Authorizedotnet,
connector::Bitpay,
connector::Braintree,
connector::Checkout,
connector::Coinbase,
@ -153,6 +154,7 @@ default_imp_for_create_customer!(
connector::Airwallex,
connector::Authorizedotnet,
connector::Bambora,
connector::Bitpay,
connector::Braintree,
connector::Checkout,
connector::Coinbase,
@ -203,6 +205,7 @@ default_imp_for_connector_redirect_response!(
connector::Aci,
connector::Adyen,
connector::Authorizedotnet,
connector::Bitpay,
connector::Braintree,
connector::Coinbase,
connector::Cybersource,
@ -239,6 +242,7 @@ default_imp_for_connector_request_id!(
connector::Airwallex,
connector::Authorizedotnet,
connector::Bambora,
connector::Bitpay,
connector::Bluesnap,
connector::Braintree,
connector::Checkout,
@ -290,6 +294,7 @@ default_imp_for_accept_dispute!(
connector::Airwallex,
connector::Authorizedotnet,
connector::Bambora,
connector::Bitpay,
connector::Bluesnap,
connector::Braintree,
connector::Coinbase,
@ -350,6 +355,7 @@ default_imp_for_file_upload!(
connector::Airwallex,
connector::Authorizedotnet,
connector::Bambora,
connector::Bitpay,
connector::Bluesnap,
connector::Braintree,
connector::Coinbase,
@ -400,6 +406,7 @@ default_imp_for_submit_evidence!(
connector::Airwallex,
connector::Authorizedotnet,
connector::Bambora,
connector::Bitpay,
connector::Bluesnap,
connector::Braintree,
connector::Cybersource,
@ -450,6 +457,7 @@ default_imp_for_defend_dispute!(
connector::Airwallex,
connector::Authorizedotnet,
connector::Bambora,
connector::Bitpay,
connector::Bluesnap,
connector::Braintree,
connector::Cybersource,

View File

@ -61,6 +61,7 @@ pub mod headers {
pub const X_TRANS_KEY: &str = "X-Trans-Key";
pub const X_VERSION: &str = "X-Version";
pub const X_CC_VERSION: &str = "X-CC-Version";
pub const X_ACCEPT_VERSION: &str = "X-Accept-Version";
pub const X_DATE: &str = "X-Date";
}

View File

@ -203,6 +203,7 @@ impl ConnectorData {
"airwallex" => Ok(Box::new(&connector::Airwallex)),
"authorizedotnet" => Ok(Box::new(&connector::Authorizedotnet)),
"bambora" => Ok(Box::new(&connector::Bambora)),
"bitpay" => Ok(Box::new(&connector::Bitpay)),
"bluesnap" => Ok(Box::new(&connector::Bluesnap)),
"braintree" => Ok(Box::new(&connector::Braintree)),
"checkout" => Ok(Box::new(&connector::Checkout)),