Type of Change
Description
This PR is a part of v2 implementation of refunds. This PR contains refunds create core flow, i.e we can be able to create refunds for payments in v2.
Important
We are movingrefunds/transformers.rs
andrefunds/validator.rs
to a separate location because of accessibility
to both v1 and v2 refund code.
Additional Changes
Motivation and Context
Merging this PR would add support for creating refunds for payments in v2 apis.
How did you test it?
- Create Payment Intent api call
curl --location 'http://localhost:8080/v2/payments/create-intent' \--header 'api-key: xyz' \--header 'Content-Type: application/json' \--header 'x-profile-id: pro_' \--header 'Authorization: api-key=xyz' \--data-raw '{ "amount_details": { "order_amount": 100, "currency": "USD" }, "capture_method":"automatic", "authentication_type": "no_three_ds", "billing": { "address": { "first_name": "John", "last_name": "Dough" }, "email": "example@example.com" }, "shipping": { "address": { "first_name": "John", "last_name": "Dough", "city": "Karwar", "zip": "581301", "state": "Karnataka" }, "email": "example@example.com" }}'
- Response from the above call
{"id":"12345_pay_0195d13dd6497c12a3b187039fc4b71e","status":"requires_payment_method","amount_details": {"order_amount":100,"currency":"USD","shipping_cost":null,"order_tax_amount":null,"external_tax_calculation":"skip","surcharge_calculation":"skip","surcharge_amount":null,"tax_on_surcharge":null },"client_secret":"12345_pay_0195d13dd6497c12a3b187039fc4b71e_secret_0195d13dd6547a938bfe7b97b36203a5","profile_id":"pro_TujgCYCsvWvO6YNsRAmC","merchant_reference_id":null,"routing_algorithm_id":null,"capture_method":"automatic","authentication_type":"no_three_ds","billing": {"address": {"city":null,"country":null,"line1":null,"line2":null,"line3":null,"zip":null,"state":null,"first_name":"John","last_name":"Dough" },"phone":null,"email":"example@example.com" },"shipping": {"address": {"city":"Karwar","country":null,"line1":null,"line2":null,"line3":null,"zip":"581301","state":"Karnataka","first_name":"John","last_name":"Dough" },"phone":null,"email":"example@example.com" },"customer_id":null,"customer_present":"present","description":null,"return_url":null,"setup_future_usage":"on_session","apply_mit_exemption":"Skip","statement_descriptor":null,"order_details":null,"allowed_payment_method_types":null,"metadata":null,"connector_metadata":null,"feature_metadata":null,"payment_link_enabled":"Skip","payment_link_config":null,"request_incremental_authorization":"default","expires_on":"2025-03-26T07:12:25.588Z","frm_metadata":null,"request_external_three_ds_authentication":"Skip"}
curl --location 'http://localhost:8080/v2/payments/12345_pay_0195d13dd6497c12a3b187039fc4b71e/confirm-intent' \--header 'x-client-secret: 12345_pay_0195d13dd6497c12a3b187039fc4b71e_secret_0195d13dd6547a938bfe7b97b36203a5' \--header 'x-profile-id: pro_TujgCYCsvWvO6YNsRAmC' \--header 'Content-Type: application/json' \--header 'Authorization: publishable-key=pk_dev_58559cdf90434adcb0b92eeb6a8b633f,client-secret=12345_pay_0195d13dd6497c12a3b187039fc4b71e_secret_0195d13dd6547a938bfe7b97b36203a5' \--data '{ "payment_method_data": { "card": { "card_number": "4242424242424242", "card_exp_month": "09", "card_exp_year": "25", "card_holder_name": "John Doe", "card_cvc": "100" } }, "payment_method_type": "card", "payment_method_subtype": "credit"}'
- Response from the above call
{"id":"12345_pay_0195d13dd6497c12a3b187039fc4b71e","status":"succeeded","amount": {"order_amount":100,"currency":"USD","shipping_cost":null,"order_tax_amount":null,"external_tax_calculation":"skip","surcharge_calculation":"skip","surcharge_amount":null,"tax_on_surcharge":null,"net_amount":100,"amount_to_capture":null,"amount_capturable":0,"amount_captured":100 },"customer_id":null,"connector":"stripe","client_secret":"12345_pay_0195d13dd6497c12a3b187039fc4b71e_secret_0195d13dd6547a938bfe7b97b36203a5","created":"2025-03-26T06:57:25.588Z","payment_method_data": {"billing":null },"payment_method_type":"card","payment_method_subtype":"credit","connector_transaction_id":"pi_3R6ntzD5R7gDAGff0MYUxZFn","connector_reference_id":null,"merchant_connector_id":"mca_FhEKUEAUdgv7mVCs3A9U","browser_info":null,"error":null,"shipping":null,"billing":null,"attempts":null,"connector_token_details": {"token":"pm_1R6ntzD5R7gDAGff07DogD0c","connector_token_request_reference_id":"oMf8ppmFul0oTqI6Zl" },"payment_method_id":null,"next_action":null,"return_url":"https://google.com/success","authentication_type":"no_three_ds","authentication_type_applied":"no_three_ds"}
- Create refund for a successful payment
curl --location 'http://localhost:8080/v2/refunds' \--header 'X-Profile-Id: pro_TujgCYCsvWvO6YNsRAmC' \--header 'Content-Type: application/json' \--header 'api-key: dev_kksGlKY14xNypCzsrJIXFCzL3t2j5kUs7ChmloPryJsdRy2RWYljonzPCxJSDmuZ' \--data '{ "payment_id":"12345_pay_0195d13dd6497c12a3b187039fc4b71e", "merchant_reference_id":"1742972385", "reason":"Paid by mistake", "metadata":{ "foo":"bar" }}'
- Response from the above call
{"id":"12345_ref_0195d13dfedb7790bc1a84be825e64d5","payment_id":"12345_pay_0195d13dd6497c12a3b187039fc4b71e","merchant_reference_id":"1742972256","amount":100,"currency":"USD","status":"succeeded","reason":"Paid by mistake","metadata": {"foo":"bar" },"error_details": {"code":"","message":"" },"created_at":"2025-03-26T06:57:35.993Z","updated_at":"2025-03-26T06:57:36.981Z","connector":"stripe","profile_id":"pro_TujgCYCsvWvO6YNsRAmC","merchant_connector_id":"mca_FhEKUEAUdgv7mVCs3A9U","connector_refund_reference_id":"12345_ref_0195d13dfedb7790bc1a84be825e64d5"}
- Trying the same refund again will give us this response
{"error":{"type":"invalid_request","message":"The refund amount exceeds the amount captured","code":"IR_13"}}
- What if the payment was failed and we try to refund it. (Invalid card number in confirm intent call)
curl --location 'http://localhost:8080/v2/payments/12345_pay_0195d142100474c3bcb32e40935e290d/confirm-intent' \--header 'x-client-secret: 12345_pay_0195d142100474c3bcb32e40935e290d_secret_0195d14210127d30b524ec0e063b3c0a' \--header 'x-profile-id: pro_TujgCYCsvWvO6YNsRAmC' \--header 'Content-Type: application/json' \--header 'Authorization: publishable-key=pk_dev_58559cdf90434adcb0b92eeb6a8b633f,client-secret=12345_pay_0195d142100474c3bcb32e40935e290d_secret_0195d14210127d30b524ec0e063b3c0a' \--data '{ "payment_method_data": { "card": { "card_number": "4242424242424242", "card_exp_month": "01", "card_exp_year": "25", "card_holder_name": "John Doe", "card_cvc": "100" } }, "payment_method_type": "card", "payment_method_subtype": "credit"}'
- Response from the above call
{"id":"12345_pay_0195d142100474c3bcb32e40935e290d","status":"failed","amount": {"order_amount":100,"currency":"USD","shipping_cost":null,"order_tax_amount":null,"external_tax_calculation":"skip","surcharge_calculation":"skip","surcharge_amount":null,"tax_on_surcharge":null,"net_amount":100,"amount_to_capture":null,"amount_capturable":0,"amount_captured":null },"customer_id":null,"connector":"stripe","client_secret":"12345_pay_0195d142100474c3bcb32e40935e290d_secret_0195d14210127d30b524ec0e063b3c0a","created":"2025-03-26T07:02:02.514Z","payment_method_data": {"billing":null },"payment_method_type":"card","payment_method_subtype":"credit","connector_transaction_id":null,"connector_reference_id":null,"merchant_connector_id":"mca_FhEKUEAUdgv7mVCs3A9U","browser_info":null,"error": {"code":"invalid_expiry_month","message":"invalid_expiry_month","unified_code":null,"unified_message":null },"shipping":null,"billing":null,"attempts":null,"connector_token_details":null,"payment_method_id":null,"next_action":null,"return_url":"https://google.com/success","authentication_type":"no_three_ds","authentication_type_applied":"no_three_ds"}
- Trying to refund failed payment
curl --location 'http://localhost:8080/v2/refunds' \--header 'X-Profile-Id: pro_TujgCYCsvWvO6YNsRAmC' \--header 'Content-Type: application/json' \--header 'api-key: dev_kksGlKY14xNypCzsrJIXFCzL3t2j5kUs7ChmloPryJsdRy2RWYljonzPCxJSDmuZ' \--data '{ "payment_id":"12345_pay_0195d142100474c3bcb32e40935e290d", "merchant_reference_id":"1742972638", "reason":"Paid by mistake", "metadata":{ "foo":"bar" }}'
- Response from the above call
{"error": {"type":"invalid_request","message":"This Payment could not be refund because it has a status of failed. The expected state is succeeded, partially_captured","code":"IR_14" }}
Error case responses
- Connector NotImplemented Error (501)
{ "error": { "type": "invalid_request", "message": "get_url method is not implemented", "code": "IR_00" }}
- Connector NotSupported Error (400)
{ "error": { "type": "invalid_request", "message": "Payment method type not supported", "code": "IR_19", "reason": "Refund is not supported by Stripe" }}
Checklist
Type of Change
Description
This PR is a part of v2 implementation of refunds. This PR contains refunds create core flow, i.e we can be able to create refunds for payments in v2.
Important
We are moving
refunds/transformers.rs
andrefunds/validator.rs
to a separate location because of accessibilityto both v1 and v2 refund code.
Additional Changes
Motivation and Context
Merging this PR would add support for creating refunds for payments in v2 apis.
How did you test it?
Error case responses
Checklist
cargo +nightly fmt --all
cargo clippy