mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
fix: validate refund amount with amount_captured instead of amount (#3120)
This commit is contained in:
@ -33,7 +33,7 @@ pub enum StripeErrorCode {
|
|||||||
expected_format: String,
|
expected_format: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_06", message = "Refund amount exceeds the payment amount.")]
|
#[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_06", message = "The refund amount exceeds the amount captured.")]
|
||||||
RefundAmountExceedsPaymentAmount { param: String },
|
RefundAmountExceedsPaymentAmount { param: String },
|
||||||
|
|
||||||
#[error(error_type = StripeErrorType::ApiError, code = "payment_intent_authentication_failure", message = "Payment failed while processing with connector. Retry payment.")]
|
#[error(error_type = StripeErrorType::ApiError, code = "payment_intent_authentication_failure", message = "Payment failed while processing with connector. Retry payment.")]
|
||||||
|
|||||||
@ -60,7 +60,7 @@ pub enum ApiErrorResponse {
|
|||||||
CustomerRedacted,
|
CustomerRedacted,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_12", message = "Reached maximum refund attempts")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_12", message = "Reached maximum refund attempts")]
|
||||||
MaximumRefundCount,
|
MaximumRefundCount,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_13", message = "Refund amount exceeds the payment amount")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_13", message = "The refund amount exceeds the amount captured")]
|
||||||
RefundAmountExceedsPaymentAmount,
|
RefundAmountExceedsPaymentAmount,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_14", message = "This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_14", message = "This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}")]
|
||||||
PaymentUnexpectedState {
|
PaymentUnexpectedState {
|
||||||
|
|||||||
@ -65,7 +65,7 @@ impl ErrorSwitch<api_models::errors::types::ApiErrorResponse> for ApiErrorRespon
|
|||||||
}
|
}
|
||||||
Self::MaximumRefundCount => AER::BadRequest(ApiError::new("IR", 12, "Reached maximum refund attempts", None)),
|
Self::MaximumRefundCount => AER::BadRequest(ApiError::new("IR", 12, "Reached maximum refund attempts", None)),
|
||||||
Self::RefundAmountExceedsPaymentAmount => {
|
Self::RefundAmountExceedsPaymentAmount => {
|
||||||
AER::BadRequest(ApiError::new("IR", 13, "Refund amount exceeds the payment amount", None))
|
AER::BadRequest(ApiError::new("IR", 13, "The refund amount exceeds the amount captured", None))
|
||||||
}
|
}
|
||||||
Self::PaymentUnexpectedState {
|
Self::PaymentUnexpectedState {
|
||||||
current_flow,
|
current_flow,
|
||||||
|
|||||||
@ -610,7 +610,11 @@ pub async fn validate_and_create_refund(
|
|||||||
),
|
),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
validator::validate_refund_amount(payment_attempt.amount, &all_refunds, refund_amount)
|
let total_amount_captured = payment_intent
|
||||||
|
.amount_captured
|
||||||
|
.unwrap_or(payment_attempt.amount);
|
||||||
|
|
||||||
|
validator::validate_refund_amount(total_amount_captured, &all_refunds, refund_amount)
|
||||||
.change_context(errors::ApiErrorResponse::RefundAmountExceedsPaymentAmount)?;
|
.change_context(errors::ApiErrorResponse::RefundAmountExceedsPaymentAmount)?;
|
||||||
|
|
||||||
validator::validate_maximum_refund_against_payment_attempt(
|
validator::validate_maximum_refund_against_payment_attempt(
|
||||||
|
|||||||
@ -17,7 +17,7 @@ pub const DEFAULT_LIMIT: i64 = 10;
|
|||||||
pub enum RefundValidationError {
|
pub enum RefundValidationError {
|
||||||
#[error("The payment attempt was not successful")]
|
#[error("The payment attempt was not successful")]
|
||||||
UnsuccessfulPaymentAttempt,
|
UnsuccessfulPaymentAttempt,
|
||||||
#[error("The refund amount exceeds the payment amount")]
|
#[error("The refund amount exceeds the amount captured")]
|
||||||
RefundAmountExceedsPaymentAmount,
|
RefundAmountExceedsPaymentAmount,
|
||||||
#[error("The order has expired")]
|
#[error("The order has expired")]
|
||||||
OrderExpired,
|
OrderExpired,
|
||||||
@ -40,7 +40,7 @@ pub fn validate_success_transaction(
|
|||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub fn validate_refund_amount(
|
pub fn validate_refund_amount(
|
||||||
payment_attempt_amount: i64, // &storage::PaymentAttempt,
|
amount_captured: i64,
|
||||||
all_refunds: &[storage::Refund],
|
all_refunds: &[storage::Refund],
|
||||||
refund_amount: i64,
|
refund_amount: i64,
|
||||||
) -> CustomResult<(), RefundValidationError> {
|
) -> CustomResult<(), RefundValidationError> {
|
||||||
@ -58,7 +58,7 @@ pub fn validate_refund_amount(
|
|||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
utils::when(
|
utils::when(
|
||||||
refund_amount > (payment_attempt_amount - total_refunded_amount),
|
refund_amount > (amount_captured - total_refunded_amount),
|
||||||
|| {
|
|| {
|
||||||
Err(report!(
|
Err(report!(
|
||||||
RefundValidationError::RefundAmountExceedsPaymentAmount
|
RefundValidationError::RefundAmountExceedsPaymentAmount
|
||||||
|
|||||||
@ -20,7 +20,7 @@ pub enum DummyConnectorErrors {
|
|||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "DC_02", message = "Missing required param: {field_name}")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "DC_02", message = "Missing required param: {field_name}")]
|
||||||
MissingRequiredField { field_name: &'static str },
|
MissingRequiredField { field_name: &'static str },
|
||||||
|
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "DC_03", message = "Refund amount exceeds the payment amount")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "DC_03", message = "The refund amount exceeds the amount captured")]
|
||||||
RefundAmountExceedsPaymentAmount,
|
RefundAmountExceedsPaymentAmount,
|
||||||
|
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "DC_04", message = "Card not supported. Please use test cards")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "DC_04", message = "Card not supported. Please use test cards")]
|
||||||
|
|||||||
@ -154,6 +154,6 @@ async fn exceed_refund() {
|
|||||||
let message: serde_json::Value = user_client.create_refund(&server, &payment_id, 100).await;
|
let message: serde_json::Value = user_client.create_refund(&server, &payment_id, 100).await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
message["error"]["message"],
|
message["error"]["message"],
|
||||||
"Refund amount exceeds the payment amount."
|
"The refund amount exceeds the amount captured."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,10 +50,10 @@ if (jsonData?.error?.type) {
|
|||||||
// Response body should have value "invalid_request" for "error type"
|
// Response body should have value "invalid_request" for "error type"
|
||||||
if (jsonData?.error?.message) {
|
if (jsonData?.error?.message) {
|
||||||
pm.test(
|
pm.test(
|
||||||
"[POST]::/payments - Content check if value for 'error.message' matches 'Refund amount exceeds the payment amount'",
|
"[POST]::/payments - Content check if value for 'error.message' matches 'The refund amount exceeds the amount captured'",
|
||||||
function () {
|
function () {
|
||||||
pm.expect(jsonData.error.message).to.eql(
|
pm.expect(jsonData.error.message).to.eql(
|
||||||
"Refund amount exceeds the payment amount",
|
"The refund amount exceeds the amount captured",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -32,9 +32,9 @@ if (jsonData?.refund_id) {
|
|||||||
// Response body should have value "succeeded" for "status"
|
// Response body should have value "succeeded" for "status"
|
||||||
if (jsonData?.error.message) {
|
if (jsonData?.error.message) {
|
||||||
pm.test(
|
pm.test(
|
||||||
"[POST]::/refunds - Content check if value for 'message' matches 'Refund amount exceeds the payment amount'",
|
"[POST]::/refunds - Content check if value for 'message' matches 'The refund amount exceeds the amount captured'",
|
||||||
function () {
|
function () {
|
||||||
pm.expect(jsonData.error.message).to.eql("Refund amount exceeds the payment amount");
|
pm.expect(jsonData.error.message).to.eql("The refund amount exceeds the amount captured");
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,13 +47,13 @@ if (jsonData?.error?.type) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response body should have value "Refund amount exceeds the payment amount" for "message"
|
// Response body should have value "The refund amount exceeds the amount captured" for "message"
|
||||||
if (jsonData?.error?.message) {
|
if (jsonData?.error?.message) {
|
||||||
pm.test(
|
pm.test(
|
||||||
"[POST]::/payments/:id/confirm - Content check if value for 'error.message' matches 'Refund amount exceeds the payment amount'",
|
"[POST]::/payments/:id/confirm - Content check if value for 'error.message' matches 'The refund amount exceeds the amount captured'",
|
||||||
function () {
|
function () {
|
||||||
pm.expect(jsonData.error.message).to.eql(
|
pm.expect(jsonData.error.message).to.eql(
|
||||||
"Refund amount exceeds the payment amount",
|
"The refund amount exceeds the amount captured",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"childrenOrder": [
|
||||||
|
"Payments - Create",
|
||||||
|
"Payments - Capture",
|
||||||
|
"Payments - Retrieve",
|
||||||
|
"Refunds - Create"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"eventOrder": ["event.test.js"]
|
||||||
|
}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
// Validate status 2xx
|
||||||
|
pm.test("[POST]::/payments/:id/capture - Status code is 2xx", function () {
|
||||||
|
pm.response.to.be.success;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate if response header has matching content-type
|
||||||
|
pm.test(
|
||||||
|
"[POST]::/payments/:id/capture - 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/:id/capture - 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/capture - Content check if value for 'status' matches 'partially_captured'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.status).to.eql("partially_captured");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response body should have value "6540" for "amount"
|
||||||
|
if (jsonData?.amount) {
|
||||||
|
pm.test(
|
||||||
|
"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.amount).to.eql(6540);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response body should have value "6000" for "amount_received"
|
||||||
|
if (jsonData?.amount_received) {
|
||||||
|
pm.test(
|
||||||
|
"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.amount_received).to.eql(6000);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"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_to_capture": 6000,
|
||||||
|
"statement_descriptor_name": "Joseph",
|
||||||
|
"statement_descriptor_suffix": "JS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/payments/:id/capture",
|
||||||
|
"host": ["{{baseUrl}}"],
|
||||||
|
"path": ["payments", ":id", "capture"],
|
||||||
|
"variable": [
|
||||||
|
{
|
||||||
|
"key": "id",
|
||||||
|
"value": "{{payment_id}}",
|
||||||
|
"description": "(Required) unique payment id"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": "To capture the funds for an uncaptured payment"
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@ -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 "requires_capture" for "status"
|
||||||
|
if (jsonData?.status) {
|
||||||
|
pm.test(
|
||||||
|
"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.status).to.eql("requires_capture");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"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": 6540,
|
||||||
|
"currency": "USD",
|
||||||
|
"confirm": true,
|
||||||
|
"capture_method": "manual",
|
||||||
|
"capture_on": "2022-09-10T10:11:12Z",
|
||||||
|
"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": "card",
|
||||||
|
"payment_method_data": {
|
||||||
|
"card": {
|
||||||
|
"card_number": "4242424242424242",
|
||||||
|
"card_exp_month": "10",
|
||||||
|
"card_exp_year": "25",
|
||||||
|
"card_holder_name": "joseph Doe",
|
||||||
|
"card_cvc": "123"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"billing": {
|
||||||
|
"address": {
|
||||||
|
"line1": "1467",
|
||||||
|
"line2": "Harrison Street",
|
||||||
|
"line3": "Harrison Street",
|
||||||
|
"city": "San Fransico",
|
||||||
|
"state": "California",
|
||||||
|
"zip": "94122",
|
||||||
|
"country": "US",
|
||||||
|
"first_name": "sundari"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shipping": {
|
||||||
|
"address": {
|
||||||
|
"line1": "1467",
|
||||||
|
"line2": "Harrison Street",
|
||||||
|
"line3": "Harrison Street",
|
||||||
|
"city": "San Fransico",
|
||||||
|
"state": "California",
|
||||||
|
"zip": "94122",
|
||||||
|
"country": "US",
|
||||||
|
"first_name": "sundari"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statement_descriptor_name": "joseph",
|
||||||
|
"statement_descriptor_suffix": "JS",
|
||||||
|
"metadata": {
|
||||||
|
"udf1": "value1",
|
||||||
|
"new_customer": "true",
|
||||||
|
"login_date": "2019-09-10T10:11:12Z"
|
||||||
|
},
|
||||||
|
"routing": {
|
||||||
|
"type": "single",
|
||||||
|
"data": "stripe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 'partially_captured'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.status).to.eql("partially_captured");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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 @@
|
|||||||
|
[]
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"eventOrder": ["event.test.js"]
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
// Validate status 4xx
|
||||||
|
pm.test("[POST]::/refunds - Status code is 4xx", function () {
|
||||||
|
pm.response.to.be.error;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate if response header has matching content-type
|
||||||
|
pm.test("[POST]::/refunds - Content-Type is application/json", function () {
|
||||||
|
pm.expect(pm.response.headers.get("Content-Type")).to.include(
|
||||||
|
"application/json",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set response object as internal variable
|
||||||
|
let jsonData = {};
|
||||||
|
try {
|
||||||
|
jsonData = pm.response.json();
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// pm.collectionVariables - Set refund_id as variable for jsonData.payment_id
|
||||||
|
if (jsonData?.refund_id) {
|
||||||
|
pm.collectionVariables.set("refund_id", jsonData.refund_id);
|
||||||
|
console.log(
|
||||||
|
"- use {{refund_id}} as collection variable for value",
|
||||||
|
jsonData.refund_id,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"INFO - Unable to assign variable {{refund_id}}, as jsonData.refund_id is undefined.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response body should have "error"
|
||||||
|
pm.test(
|
||||||
|
"[POST]::/payments/:id/confirm - Content check if 'error' exists",
|
||||||
|
function () {
|
||||||
|
pm.expect(typeof jsonData.error !== "undefined").to.be.true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Response body should have value "invalid_request" for "error type"
|
||||||
|
if (jsonData?.error?.type) {
|
||||||
|
pm.test(
|
||||||
|
"[POST]::/payments/:id/confirm - Content check if value for 'error.type' matches 'invalid_request'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.error.type).to.eql("invalid_request");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response body should have value "The refund amount exceeds the amount captured" for "error message"
|
||||||
|
if (jsonData?.error?.type) {
|
||||||
|
pm.test(
|
||||||
|
"[POST]::/payments/:id/confirm - Content check if value for 'error.message' matches 'The refund amount exceeds the amount captured'",
|
||||||
|
function () {
|
||||||
|
pm.expect(jsonData.error.message).to.eql("The refund amount exceeds the amount captured");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Accept",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"options": {
|
||||||
|
"raw": {
|
||||||
|
"language": "json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"raw_json_formatted": {
|
||||||
|
"payment_id": "{{payment_id}}",
|
||||||
|
"amount": 6540,
|
||||||
|
"reason": "Customer returned product",
|
||||||
|
"refund_type": "instant",
|
||||||
|
"metadata": {
|
||||||
|
"udf1": "value1",
|
||||||
|
"new_customer": "true",
|
||||||
|
"login_date": "2019-09-10T10:11:12Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/refunds",
|
||||||
|
"host": ["{{baseUrl}}"],
|
||||||
|
"path": ["refunds"]
|
||||||
|
},
|
||||||
|
"description": "To create a refund against an already processed payment"
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@ -13610,10 +13610,10 @@
|
|||||||
"// Response body should have value \"invalid_request\" for \"error type\"",
|
"// Response body should have value \"invalid_request\" for \"error type\"",
|
||||||
"if (jsonData?.error?.message) {",
|
"if (jsonData?.error?.message) {",
|
||||||
" pm.test(",
|
" pm.test(",
|
||||||
" \"[POST]::/payments - Content check if value for 'error.message' matches 'Refund amount exceeds the payment amount'\",",
|
" \"[POST]::/payments - Content check if value for 'error.message' matches 'The refund amount exceeds the amount captured'\",",
|
||||||
" function () {",
|
" function () {",
|
||||||
" pm.expect(jsonData.error.message).to.eql(",
|
" pm.expect(jsonData.error.message).to.eql(",
|
||||||
" \"Refund amount exceeds the payment amount\",",
|
" \"The refund amount exceeds the amount captured\",",
|
||||||
" );",
|
" );",
|
||||||
" },",
|
" },",
|
||||||
" );",
|
" );",
|
||||||
|
|||||||
@ -4927,9 +4927,9 @@
|
|||||||
"// Response body should have value \"succeeded\" for \"status\"",
|
"// Response body should have value \"succeeded\" for \"status\"",
|
||||||
"if (jsonData?.error.message) {",
|
"if (jsonData?.error.message) {",
|
||||||
" pm.test(",
|
" pm.test(",
|
||||||
" \"[POST]::/refunds - Content check if value for 'message' matches 'Refund amount exceeds the payment amount'\",",
|
" \"[POST]::/refunds - Content check if value for 'message' matches 'The refund amount exceeds the amount captured'\",",
|
||||||
" function () {",
|
" function () {",
|
||||||
" pm.expect(jsonData.error.message).to.eql(\"Refund amount exceeds the payment amount\");",
|
" pm.expect(jsonData.error.message).to.eql(\"The refund amount exceeds the amount captured\");",
|
||||||
" },",
|
" },",
|
||||||
" );",
|
" );",
|
||||||
"}",
|
"}",
|
||||||
|
|||||||
@ -13364,13 +13364,13 @@
|
|||||||
" );",
|
" );",
|
||||||
"}",
|
"}",
|
||||||
"",
|
"",
|
||||||
"// Response body should have value \"Refund amount exceeds the payment amount\" for \"message\"",
|
"// Response body should have value \"The refund amount exceeds the amount captured\" for \"message\"",
|
||||||
"if (jsonData?.error?.message) {",
|
"if (jsonData?.error?.message) {",
|
||||||
" pm.test(",
|
" pm.test(",
|
||||||
" \"[POST]::/payments/:id/confirm - Content check if value for 'error.message' matches 'Refund amount exceeds the payment amount'\",",
|
" \"[POST]::/payments/:id/confirm - Content check if value for 'error.message' matches 'The refund amount exceeds the amount captured'\",",
|
||||||
" function () {",
|
" function () {",
|
||||||
" pm.expect(jsonData.error.message).to.eql(",
|
" pm.expect(jsonData.error.message).to.eql(",
|
||||||
" \"Refund amount exceeds the payment amount\",",
|
" \"The refund amount exceeds the amount captured\",",
|
||||||
" );",
|
" );",
|
||||||
" },",
|
" },",
|
||||||
" );",
|
" );",
|
||||||
|
|||||||
@ -1583,7 +1583,7 @@
|
|||||||
"responseBodyTests": [
|
"responseBodyTests": [
|
||||||
{
|
{
|
||||||
"key": "message",
|
"key": "message",
|
||||||
"value": "Refund amount exceeds the payment amount"
|
"value": "The refund amount exceeds the amount captured"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user