mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
fix(connector): [Adyen] ErrorHandling in case of Balance Check for Gift Cards (#1976)
This commit is contained in:
@ -14,11 +14,8 @@ use crate::{
|
||||
configs::settings,
|
||||
connector::utils as connector_utils,
|
||||
consts,
|
||||
core::{
|
||||
self,
|
||||
errors::{self, CustomResult},
|
||||
},
|
||||
headers, logger, routes,
|
||||
core::errors::{self, CustomResult},
|
||||
headers, logger,
|
||||
services::{
|
||||
self,
|
||||
request::{self, Mask},
|
||||
@ -560,7 +557,6 @@ impl
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Authorize,
|
||||
@ -568,49 +564,6 @@ impl
|
||||
types::PaymentsResponseData,
|
||||
> for Adyen
|
||||
{
|
||||
async fn execute_pretasks(
|
||||
&self,
|
||||
router_data: &mut types::PaymentsAuthorizeRouterData,
|
||||
app_state: &routes::AppState,
|
||||
) -> CustomResult<(), errors::ConnectorError> {
|
||||
match &router_data.request.payment_method_data {
|
||||
api_models::payments::PaymentMethodData::GiftCard(gift_card_data) => {
|
||||
match gift_card_data.as_ref() {
|
||||
api_models::payments::GiftCardData::Givex(_) => {
|
||||
let integ: Box<
|
||||
&(dyn services::ConnectorIntegration<
|
||||
api::Balance,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
> + Send
|
||||
+ Sync
|
||||
+ 'static),
|
||||
> = Box::new(&Self);
|
||||
|
||||
let authorize_data = &types::PaymentsBalanceRouterData::from((
|
||||
&router_data.to_owned(),
|
||||
router_data.request.clone(),
|
||||
));
|
||||
|
||||
let resp = services::execute_connector_processing_step(
|
||||
app_state,
|
||||
integ,
|
||||
authorize_data,
|
||||
core::payments::CallConnectorAction::Trigger,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
router_data.payment_method_balance = resp.payment_method_balance;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
@ -667,7 +620,6 @@ impl
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
check_for_payment_method_balance(req)?;
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
@ -725,28 +677,23 @@ impl
|
||||
}
|
||||
}
|
||||
|
||||
impl api::PaymentsPreProcessing for Adyen {}
|
||||
|
||||
impl
|
||||
services::ConnectorIntegration<
|
||||
api::Balance,
|
||||
types::PaymentsAuthorizeData,
|
||||
api::PreProcessing,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
> for Adyen
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsBalanceRouterData,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError>
|
||||
where
|
||||
Self: services::ConnectorIntegration<
|
||||
api::Balance,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
{
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
|
||||
let mut header = vec![(
|
||||
headers::CONTENT_TYPE.to_string(),
|
||||
types::PaymentsBalanceType::get_content_type(self)
|
||||
types::PaymentsPreProcessingType::get_content_type(self)
|
||||
.to_string()
|
||||
.into(),
|
||||
)];
|
||||
@ -757,7 +704,7 @@ impl
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsBalanceRouterData,
|
||||
_req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
@ -768,7 +715,7 @@ impl
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsBalanceRouterData,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
|
||||
let connector_req = adyen::AdyenBalanceRequest::try_from(req)?;
|
||||
@ -783,18 +730,20 @@ impl
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &types::PaymentsBalanceRouterData,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||
Ok(Some(
|
||||
services::RequestBuilder::new()
|
||||
.method(services::Method::Post)
|
||||
.url(&types::PaymentsBalanceType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsBalanceType::get_headers(
|
||||
.url(&types::PaymentsPreProcessingType::get_url(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsBalanceType::get_request_body(
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsPreProcessingType::get_headers(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.body(types::PaymentsPreProcessingType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build(),
|
||||
@ -803,13 +752,40 @@ impl
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &types::PaymentsBalanceRouterData,
|
||||
data: &types::PaymentsPreProcessingRouterData,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::PaymentsBalanceRouterData, errors::ConnectorError> {
|
||||
) -> CustomResult<types::PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: adyen::AdyenBalanceResponse = res
|
||||
.response
|
||||
.parse_struct("AdyenBalanceResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
|
||||
let currency = match data.request.currency {
|
||||
Some(currency) => currency,
|
||||
None => Err(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "currency",
|
||||
})?,
|
||||
};
|
||||
let amount = match data.request.amount {
|
||||
Some(amount) => amount,
|
||||
None => Err(errors::ConnectorError::MissingRequiredField {
|
||||
field_name: "amount",
|
||||
})?,
|
||||
};
|
||||
|
||||
if response.balance.currency != currency || response.balance.value < amount {
|
||||
Ok(types::RouterData {
|
||||
response: Err(types::ErrorResponse {
|
||||
code: consts::NO_ERROR_CODE.to_string(),
|
||||
message: consts::NO_ERROR_MESSAGE.to_string(),
|
||||
reason: Some(consts::LOW_BALANCE_ERROR_MESSAGE.to_string()),
|
||||
status_code: res.status_code,
|
||||
attempt_status: Some(enums::AttemptStatus::Failure),
|
||||
connector_transaction_id: None,
|
||||
}),
|
||||
..data.clone()
|
||||
})
|
||||
} else {
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
@ -817,6 +793,7 @@ impl
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
@ -1634,7 +1611,7 @@ impl api::IncomingWebhook for Adyen {
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
Ok(api::disputes::DisputePayload {
|
||||
amount: notif.amount.value.to_string(),
|
||||
currency: notif.amount.currency,
|
||||
currency: notif.amount.currency.to_string(),
|
||||
dispute_stage: api_models::enums::DisputeStage::from(notif.event_code.clone()),
|
||||
connector_dispute_id: notif.psp_reference,
|
||||
connector_reason: notif.reason,
|
||||
@ -1646,27 +1623,3 @@ impl api::IncomingWebhook for Adyen {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_for_payment_method_balance(
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
) -> CustomResult<(), errors::ConnectorError> {
|
||||
match &req.request.payment_method_data {
|
||||
api_models::payments::PaymentMethodData::GiftCard(gift_card) => match gift_card.as_ref() {
|
||||
api_models::payments::GiftCardData::Givex(_) => {
|
||||
let payment_method_balance = req
|
||||
.payment_method_balance
|
||||
.as_ref()
|
||||
.ok_or(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
if payment_method_balance.currency != req.request.currency.to_string()
|
||||
|| payment_method_balance.amount < req.request.amount
|
||||
{
|
||||
Err(errors::ConnectorError::InSufficientBalanceInPaymentMethod.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,8 +213,8 @@ pub struct AdyenBalanceRequest<'a> {
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdyenBalanceResponse {
|
||||
psp_reference: String,
|
||||
balance: Amount,
|
||||
pub psp_reference: String,
|
||||
pub balance: Amount,
|
||||
}
|
||||
|
||||
/// This implementation will be used only in Authorize, Automatic capture flow.
|
||||
@ -397,8 +397,8 @@ pub enum ActionType {
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Amount {
|
||||
currency: String,
|
||||
value: i64,
|
||||
pub currency: storage_enums::Currency,
|
||||
pub value: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
@ -1392,11 +1392,11 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&types::PaymentsBalanceRouterData> for AdyenBalanceRequest<'a> {
|
||||
impl<'a> TryFrom<&types::PaymentsPreProcessingRouterData> for AdyenBalanceRequest<'a> {
|
||||
type Error = Error;
|
||||
fn try_from(item: &types::PaymentsBalanceRouterData) -> Result<Self, Self::Error> {
|
||||
fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result<Self, Self::Error> {
|
||||
let payment_method = match &item.request.payment_method_data {
|
||||
payments::PaymentMethodData::GiftCard(gift_card_data) => {
|
||||
Some(payments::PaymentMethodData::GiftCard(gift_card_data)) => {
|
||||
match gift_card_data.as_ref() {
|
||||
payments::GiftCardData::Givex(gift_card_data) => {
|
||||
let balance_pm = BalancePmData {
|
||||
@ -1510,7 +1510,7 @@ fn get_channel_type(pm_type: &Option<storage_enums::PaymentMethodType>) -> Optio
|
||||
|
||||
fn get_amount_data(item: &AdyenRouterData<&types::PaymentsAuthorizeRouterData>) -> Amount {
|
||||
Amount {
|
||||
currency: item.router_data.request.currency.to_string(),
|
||||
currency: item.router_data.request.currency,
|
||||
value: item.amount.to_owned(),
|
||||
}
|
||||
}
|
||||
@ -2857,12 +2857,24 @@ impl TryFrom<types::PaymentsCancelResponseRouterData<AdyenCancelResponse>>
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<types::PaymentsBalanceResponseRouterData<AdyenBalanceResponse>>
|
||||
for types::PaymentsBalanceRouterData
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
AdyenBalanceResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsPreProcessingData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = Error;
|
||||
fn try_from(
|
||||
item: types::PaymentsBalanceResponseRouterData<AdyenBalanceResponse>,
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
AdyenBalanceResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
response: Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
@ -3457,7 +3469,7 @@ impl TryFrom<&AdyenRouterData<&types::PaymentsCaptureRouterData>> for AdyenCaptu
|
||||
merchant_account: auth_type.merchant_account,
|
||||
reference,
|
||||
amount: Amount {
|
||||
currency: item.router_data.request.currency.to_string(),
|
||||
currency: item.router_data.request.currency,
|
||||
value: item.amount.to_owned(),
|
||||
},
|
||||
})
|
||||
@ -3547,7 +3559,7 @@ impl<F> TryFrom<&AdyenRouterData<&types::RefundsRouterData<F>>> for AdyenRefundR
|
||||
Ok(Self {
|
||||
merchant_account: auth_type.merchant_account,
|
||||
amount: Amount {
|
||||
currency: item.router_data.request.currency.to_string(),
|
||||
currency: item.router_data.request.currency,
|
||||
value: item.router_data.request.refund_amount,
|
||||
},
|
||||
merchant_refund_reason: item.router_data.request.reason.clone(),
|
||||
@ -3629,7 +3641,7 @@ pub struct AdyenAdditionalDataWH {
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct AdyenAmountWH {
|
||||
pub value: i64,
|
||||
pub currency: String,
|
||||
pub currency: storage_enums::Currency,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, strum::Display, PartialEq)]
|
||||
@ -3955,7 +3967,7 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutE
|
||||
)?;
|
||||
Ok(Self {
|
||||
amount: Amount {
|
||||
currency: item.router_data.request.destination_currency.to_string(),
|
||||
currency: item.router_data.request.destination_currency,
|
||||
value: item.amount.to_owned(),
|
||||
},
|
||||
merchant_account: auth_type.merchant_account,
|
||||
@ -4030,7 +4042,7 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutC
|
||||
Ok(Self {
|
||||
amount: Amount {
|
||||
value: item.amount.to_owned(),
|
||||
currency: item.router_data.request.destination_currency.to_string(),
|
||||
currency: item.router_data.request.destination_currency,
|
||||
},
|
||||
recurring: RecurringContract {
|
||||
contract: Contract::Payout,
|
||||
@ -4077,7 +4089,7 @@ impl<F> TryFrom<&AdyenRouterData<&types::PayoutsRouterData<F>>> for AdyenPayoutF
|
||||
Ok(Self::Card(Box::new(PayoutFulfillCardRequest {
|
||||
amount: Amount {
|
||||
value: item.amount.to_owned(),
|
||||
currency: item.router_data.request.destination_currency.to_string(),
|
||||
currency: item.router_data.request.destination_currency,
|
||||
},
|
||||
card: get_payout_card_details(&item.router_data.get_payout_method_data()?)
|
||||
.map_or(
|
||||
|
||||
@ -27,6 +27,7 @@ pub const DEFAULT_FULFILLMENT_TIME: i64 = 15 * 60;
|
||||
pub(crate) const NO_ERROR_MESSAGE: &str = "No error message";
|
||||
pub(crate) const NO_ERROR_CODE: &str = "No error code";
|
||||
pub(crate) const UNSUPPORTED_ERROR_MESSAGE: &str = "Unsupported response type";
|
||||
pub(crate) const LOW_BALANCE_ERROR_MESSAGE: &str = "Insufficient balance in the payment method";
|
||||
pub(crate) const CONNECTOR_UNAUTHORIZED_ERROR: &str = "Authentication Error from the connector";
|
||||
pub(crate) const CANNOT_CONTINUE_AUTH: &str =
|
||||
"Cannot continue with Authorization due to failed Liability Shift.";
|
||||
|
||||
@ -1408,6 +1408,17 @@ where
|
||||
(router_data, should_continue_payment)
|
||||
}
|
||||
}
|
||||
Some(api_models::payments::PaymentMethodData::GiftCard(_)) => {
|
||||
if connector.connector_name == router_types::Connector::Adyen {
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
|
||||
let is_error_in_response = router_data.response.is_err();
|
||||
// If is_error_in_response is true, should_continue_payment should be false, we should throw the error
|
||||
(router_data, !is_error_in_response)
|
||||
} else {
|
||||
(router_data, should_continue_payment)
|
||||
}
|
||||
}
|
||||
Some(api_models::payments::PaymentMethodData::BankDebit(_)) => {
|
||||
if connector.connector_name == router_types::Connector::Gocardless {
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
|
||||
@ -832,7 +832,6 @@ impl<const T: u8>
|
||||
|
||||
default_imp_for_pre_processing_steps!(
|
||||
connector::Aci,
|
||||
connector::Adyen,
|
||||
connector::Airwallex,
|
||||
connector::Authorizedotnet,
|
||||
connector::Bambora,
|
||||
|
||||
@ -323,7 +323,7 @@ pub struct ApplePayCryptogramData {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PaymentMethodBalance {
|
||||
pub amount: i64,
|
||||
pub currency: String,
|
||||
pub currency: storage_enums::Currency,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"childrenOrder": ["Payments - Create", "Payments - Retrieve"]
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"eventOrder": ["event.test.js"]
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
// Validate status 2xx
|
||||
pm.test("[POST]::/payments - Status code is 2xx", function () {
|
||||
pm.response.to.be.success;
|
||||
});
|
||||
|
||||
// Validate if response header has matching content-type
|
||||
pm.test("[POST]::/payments - Content-Type is application/json", function () {
|
||||
pm.expect(pm.response.headers.get("Content-Type")).to.include(
|
||||
"application/json",
|
||||
);
|
||||
});
|
||||
|
||||
// Validate if response has JSON Body
|
||||
pm.test("[POST]::/payments - Response has JSON Body", function () {
|
||||
pm.response.to.have.jsonBody();
|
||||
});
|
||||
|
||||
// Set response object as internal variable
|
||||
let jsonData = {};
|
||||
try {
|
||||
jsonData = pm.response.json();
|
||||
} catch (e) {}
|
||||
|
||||
// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id
|
||||
if (jsonData?.payment_id) {
|
||||
pm.collectionVariables.set("payment_id", jsonData.payment_id);
|
||||
console.log(
|
||||
"- use {{payment_id}} as collection variable for value",
|
||||
jsonData.payment_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id
|
||||
if (jsonData?.mandate_id) {
|
||||
pm.collectionVariables.set("mandate_id", jsonData.mandate_id);
|
||||
console.log(
|
||||
"- use {{mandate_id}} as collection variable for value",
|
||||
jsonData.mandate_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret
|
||||
if (jsonData?.client_secret) {
|
||||
pm.collectionVariables.set("client_secret", jsonData.client_secret);
|
||||
console.log(
|
||||
"- use {{client_secret}} as collection variable for value",
|
||||
jsonData.client_secret,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// Response body should have value "succeeded" for "status"
|
||||
if (jsonData?.status) {
|
||||
pm.test(
|
||||
"[POST]::/payments - Content check if value for 'status' matches 'succeeded'",
|
||||
function () {
|
||||
pm.expect(jsonData.status).to.eql("succeeded");
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
},
|
||||
"raw_json_formatted": {
|
||||
"amount": 1100,
|
||||
"currency": "EUR",
|
||||
"confirm": true,
|
||||
"capture_method": "automatic",
|
||||
"capture_on": "2022-09-10T10:11:12Z",
|
||||
"amount_to_capture": 1100,
|
||||
"customer_id": "StripeCustomer",
|
||||
"email": "guest@example.com",
|
||||
"name": "John Doe",
|
||||
"phone": "999999999",
|
||||
"phone_country_code": "+65",
|
||||
"description": "Its my first payment request",
|
||||
"authentication_type": "no_three_ds",
|
||||
"return_url": "https://duck.com",
|
||||
"payment_method": "gift_card",
|
||||
"payment_method_type": "givex",
|
||||
"payment_method_data": {
|
||||
"gift_card": {
|
||||
"givex": {
|
||||
"number": "6364530000000000",
|
||||
"cvc": "122222"
|
||||
}
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"type": "single",
|
||||
"data": "adyen"
|
||||
},
|
||||
"billing": {
|
||||
"address": {
|
||||
"line1": "1467",
|
||||
"line2": "Harrison Street",
|
||||
"line3": "Harrison Street",
|
||||
"city": "San Fransico",
|
||||
"state": "California",
|
||||
"zip": "94122",
|
||||
"country": "US",
|
||||
"first_name": "PiX"
|
||||
}
|
||||
},
|
||||
"shipping": {
|
||||
"address": {
|
||||
"line1": "1467",
|
||||
"line2": "Harrison Street",
|
||||
"line3": "Harrison Street",
|
||||
"city": "San Fransico",
|
||||
"state": "California",
|
||||
"zip": "94122",
|
||||
"country": "US",
|
||||
"first_name": "PiX"
|
||||
}
|
||||
},
|
||||
"statement_descriptor_name": "joseph",
|
||||
"statement_descriptor_suffix": "JS",
|
||||
"metadata": {
|
||||
"udf1": "value1",
|
||||
"new_customer": "true",
|
||||
"login_date": "2019-09-10T10:11:12Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/payments",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["payments"]
|
||||
},
|
||||
"description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
[]
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"eventOrder": ["event.test.js"]
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
// Validate status 2xx
|
||||
pm.test("[GET]::/payments/:id - Status code is 2xx", function () {
|
||||
pm.response.to.be.success;
|
||||
});
|
||||
|
||||
// Validate if response header has matching content-type
|
||||
pm.test("[GET]::/payments/:id - Content-Type is application/json", function () {
|
||||
pm.expect(pm.response.headers.get("Content-Type")).to.include(
|
||||
"application/json",
|
||||
);
|
||||
});
|
||||
|
||||
// Validate if response has JSON Body
|
||||
pm.test("[GET]::/payments/:id - Response has JSON Body", function () {
|
||||
pm.response.to.have.jsonBody();
|
||||
});
|
||||
|
||||
// Set response object as internal variable
|
||||
let jsonData = {};
|
||||
try {
|
||||
jsonData = pm.response.json();
|
||||
} catch (e) {}
|
||||
|
||||
// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id
|
||||
if (jsonData?.payment_id) {
|
||||
pm.collectionVariables.set("payment_id", jsonData.payment_id);
|
||||
console.log(
|
||||
"- use {{payment_id}} as collection variable for value",
|
||||
jsonData.payment_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id
|
||||
if (jsonData?.mandate_id) {
|
||||
pm.collectionVariables.set("mandate_id", jsonData.mandate_id);
|
||||
console.log(
|
||||
"- use {{mandate_id}} as collection variable for value",
|
||||
jsonData.mandate_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret
|
||||
if (jsonData?.client_secret) {
|
||||
pm.collectionVariables.set("client_secret", jsonData.client_secret);
|
||||
console.log(
|
||||
"- use {{client_secret}} as collection variable for value",
|
||||
jsonData.client_secret,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// Response body should have value "Succeeded" for "status"
|
||||
if (jsonData?.status) {
|
||||
pm.test(
|
||||
"[POST]::/payments/:id - Content check if value for 'status' matches 'succeeded'",
|
||||
function () {
|
||||
pm.expect(jsonData.status).to.eql("succeeded");
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/payments/:id?force_sync=true",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["payments", ":id"],
|
||||
"query": [
|
||||
{
|
||||
"key": "force_sync",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "id",
|
||||
"value": "{{payment_id}}",
|
||||
"description": "(Required) unique payment id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
[]
|
||||
@ -189,6 +189,18 @@
|
||||
"installment_payment_enabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"payment_method": "gift_card",
|
||||
"payment_method_types": [
|
||||
{
|
||||
"payment_method_type": "givex",
|
||||
"minimum_amount": 1,
|
||||
"maximum_amount": 68607706,
|
||||
"recurring_enabled": true,
|
||||
"installment_payment_enabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"payment_method": "bank_redirect",
|
||||
|
||||
@ -43,6 +43,10 @@
|
||||
"card_cvc": "7373"
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"type": "single",
|
||||
"data": "adyen"
|
||||
},
|
||||
"billing": {
|
||||
"address": {
|
||||
"line1": "1467",
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"childrenOrder": ["Payments - Create", "Payments - Retrieve"]
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"eventOrder": ["event.test.js"]
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
// Validate status 2xx
|
||||
pm.test("[POST]::/payments - Status code is 2xx", function () {
|
||||
pm.response.to.be.success;
|
||||
});
|
||||
|
||||
// Validate if response header has matching content-type
|
||||
pm.test("[POST]::/payments - Content-Type is application/json", function () {
|
||||
pm.expect(pm.response.headers.get("Content-Type")).to.include(
|
||||
"application/json",
|
||||
);
|
||||
});
|
||||
|
||||
// Validate if response has JSON Body
|
||||
pm.test("[POST]::/payments - Response has JSON Body", function () {
|
||||
pm.response.to.have.jsonBody();
|
||||
});
|
||||
|
||||
// Set response object as internal variable
|
||||
let jsonData = {};
|
||||
try {
|
||||
jsonData = pm.response.json();
|
||||
} catch (e) {}
|
||||
|
||||
// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id
|
||||
if (jsonData?.payment_id) {
|
||||
pm.collectionVariables.set("payment_id", jsonData.payment_id);
|
||||
console.log(
|
||||
"- use {{payment_id}} as collection variable for value",
|
||||
jsonData.payment_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id
|
||||
if (jsonData?.mandate_id) {
|
||||
pm.collectionVariables.set("mandate_id", jsonData.mandate_id);
|
||||
console.log(
|
||||
"- use {{mandate_id}} as collection variable for value",
|
||||
jsonData.mandate_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret
|
||||
if (jsonData?.client_secret) {
|
||||
pm.collectionVariables.set("client_secret", jsonData.client_secret);
|
||||
console.log(
|
||||
"- use {{client_secret}} as collection variable for value",
|
||||
jsonData.client_secret,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// Response body should have value "failed" for "status"
|
||||
if (jsonData?.status) {
|
||||
pm.test(
|
||||
"[POST]::/payments - Content check if value for 'status' matches 'failed'",
|
||||
function () {
|
||||
pm.expect(jsonData.status).to.eql("failed");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Response body should have error message as "Insufficient balance in the payment method"
|
||||
if (jsonData?.error_message) {
|
||||
pm.test(
|
||||
"[POST]::/payments - Content check if value for 'error_message' matches 'Insufficient balance in the payment method'",
|
||||
function () {
|
||||
pm.expect(jsonData.error_message).to.eql("Insufficient balance in the payment method");
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
{
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
},
|
||||
"raw_json_formatted": {
|
||||
"amount": 14100,
|
||||
"currency": "EUR",
|
||||
"confirm": true,
|
||||
"capture_method": "automatic",
|
||||
"capture_on": "2022-09-10T10:11:12Z",
|
||||
"amount_to_capture": 14100,
|
||||
"customer_id": "StripeCustomer",
|
||||
"email": "guest@example.com",
|
||||
"name": "John Doe",
|
||||
"phone": "999999999",
|
||||
"phone_country_code": "+65",
|
||||
"description": "Its my first payment request",
|
||||
"authentication_type": "no_three_ds",
|
||||
"return_url": "https://duck.com",
|
||||
"payment_method": "gift_card",
|
||||
"payment_method_type": "givex",
|
||||
"payment_method_data": {
|
||||
"gift_card": {
|
||||
"givex": {
|
||||
"number": "6364530000000000",
|
||||
"cvc": "122222"
|
||||
}
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"type": "single",
|
||||
"data": "adyen"
|
||||
},
|
||||
"billing": {
|
||||
"address": {
|
||||
"line1": "1467",
|
||||
"line2": "Harrison Street",
|
||||
"line3": "Harrison Street",
|
||||
"city": "San Fransico",
|
||||
"state": "California",
|
||||
"zip": "94122",
|
||||
"country": "US",
|
||||
"first_name": "PiX"
|
||||
}
|
||||
},
|
||||
"shipping": {
|
||||
"address": {
|
||||
"line1": "1467",
|
||||
"line2": "Harrison Street",
|
||||
"line3": "Harrison Street",
|
||||
"city": "San Fransico",
|
||||
"state": "California",
|
||||
"zip": "94122",
|
||||
"country": "US",
|
||||
"first_name": "PiX"
|
||||
}
|
||||
},
|
||||
"statement_descriptor_name": "joseph",
|
||||
"statement_descriptor_suffix": "JS",
|
||||
"metadata": {
|
||||
"udf1": "value1",
|
||||
"new_customer": "true",
|
||||
"login_date": "2019-09-10T10:11:12Z"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/payments",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["payments"]
|
||||
},
|
||||
"description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
[]
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"eventOrder": ["event.test.js"]
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
// Validate status 2xx
|
||||
pm.test("[GET]::/payments/:id - Status code is 2xx", function () {
|
||||
pm.response.to.be.success;
|
||||
});
|
||||
|
||||
// Validate if response header has matching content-type
|
||||
pm.test("[GET]::/payments/:id - Content-Type is application/json", function () {
|
||||
pm.expect(pm.response.headers.get("Content-Type")).to.include(
|
||||
"application/json",
|
||||
);
|
||||
});
|
||||
|
||||
// Validate if response has JSON Body
|
||||
pm.test("[GET]::/payments/:id - Response has JSON Body", function () {
|
||||
pm.response.to.have.jsonBody();
|
||||
});
|
||||
|
||||
// Set response object as internal variable
|
||||
let jsonData = {};
|
||||
try {
|
||||
jsonData = pm.response.json();
|
||||
} catch (e) {}
|
||||
|
||||
// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id
|
||||
if (jsonData?.payment_id) {
|
||||
pm.collectionVariables.set("payment_id", jsonData.payment_id);
|
||||
console.log(
|
||||
"- use {{payment_id}} as collection variable for value",
|
||||
jsonData.payment_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id
|
||||
if (jsonData?.mandate_id) {
|
||||
pm.collectionVariables.set("mandate_id", jsonData.mandate_id);
|
||||
console.log(
|
||||
"- use {{mandate_id}} as collection variable for value",
|
||||
jsonData.mandate_id,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret
|
||||
if (jsonData?.client_secret) {
|
||||
pm.collectionVariables.set("client_secret", jsonData.client_secret);
|
||||
console.log(
|
||||
"- use {{client_secret}} as collection variable for value",
|
||||
jsonData.client_secret,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.",
|
||||
);
|
||||
}
|
||||
|
||||
// Response body should have value "Failed" for "status"
|
||||
if (jsonData?.status) {
|
||||
pm.test(
|
||||
"[POST]::/payments/:id - Content check if value for 'status' matches 'failed'",
|
||||
function () {
|
||||
pm.expect(jsonData.status).to.eql("failed");
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/payments/:id?force_sync=true",
|
||||
"host": ["{{baseUrl}}"],
|
||||
"path": ["payments", ":id"],
|
||||
"query": [
|
||||
{
|
||||
"key": "force_sync",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "id",
|
||||
"value": "{{payment_id}}",
|
||||
"description": "(Required) unique payment id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment"
|
||||
}
|
||||
Reference in New Issue
Block a user