Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(connector): [Adyen] add dispute flows for adyen connector #5514

Merged
merged 19 commits into from
Aug 23, 2024

Conversation

KiranKBR
Copy link
Contributor

@KiranKBR KiranKBR commented Aug 2, 2024

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

This PR will add dispute flows - accept,defend,submitEvidence to adyen connector

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

How did you test it?

Accept:
image

Submit Evidence:
image

Accept request:

curl --location 'http://127.0.0.1:8080/disputes/accept/4' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_fa9dwFZSZMb3ff7RSv3pUo80lBHMK9ehompmReZFR0ChI6EdIjLqhHLgIPiPmAVT' \
--data '{
   "dispute_id" : "4",
   "connector_dispute_id" : "DZ4DPSHB4WD2WN82"
}'

response:

    "dispute_id": "4",
    "payment_id": "pay_CKkAkx5CUtC3XS70Y0zW",
    "attempt_id": "pay_CKkAkx5CUtC3XS70Y0zW_1",
    "amount": "6540",
    "currency": "USD",
    "dispute_stage": "dispute",
    "dispute_status": "dispute_accepted",
    "connector": "adyen",
    "connector_status": "open",
    "connector_dispute_id": "DZ4DPSHB4WD2WN82",
    "connector_reason": null,
    "connector_reason_code": null,
    "challenge_required_by": null,
    "connector_created_at": null,
    "connector_updated_at": null,
    "created_at": "2024-08-11T13:17:27.165Z",
    "profile_id": null,
    "merchant_connector_id": null
}

Submit evidence:

curl --location 'http://127.0.0.1:8080/disputes/evidence' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_fa9dwFZSZMb3ff7RSv3pUo80lBHMK9ehompmReZFR0ChI6EdIjLqhHLgIPiPmAVT' \
--data '{
   "dispute_id" : "4",
   "refund_policy" : "file_AB3d0rudo4VuIMdCflbE"
}'

response:

{
    "dispute_id": "4",
    "payment_id": "pay_CKkAkx5CUtC3XS70Y0zW",
    "attempt_id": "pay_CKkAkx5CUtC3XS70Y0zW_1",
    "amount": "6540",
    "currency": "USD",
    "dispute_stage": "dispute",
    "dispute_status": "dispute_challenged",
    "connector": "adyen",
    "connector_status": "open",
    "connector_dispute_id": "DZ4DPSHB4WD2WN82",
    "connector_reason": null,
    "connector_reason_code": null,
    "challenge_required_by": null,
    "connector_created_at": null,
    "connector_updated_at": null,
    "created_at": "2024-08-11T13:17:27.165Z",
    "profile_id": null,
    "merchant_connector_id": null
}

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

@KiranKBR KiranKBR requested review from a team as code owners August 2, 2024 10:33
@KiranKBR KiranKBR changed the title add dispute flows for adyen feat(connector): [Adyen] add dispute flows for adyen connector Aug 2, 2024
@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from c8f2f1c to 7d2c714 Compare August 2, 2024 12:57
@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from 7374cf8 to 8f005fc Compare August 5, 2024 11:56
@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from 82bf8da to a384d96 Compare August 6, 2024 03:50
@@ -181,6 +181,7 @@ hash_key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
aci.base_url = "https://eu-test.oppwa.com/"
adyen.base_url = "https://checkout-test.adyen.com/"
adyen.secondary_base_url = "https://pal-test.adyen.com/"
adyen.third_base_url = "https://ca-test.adyen.com/ca/services/DisputeService/v30/"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we proper naming convention stating the usecase of the newly added url?

Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't add the url path (ca/services/DisputeService/v30/) in base_url

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we proper naming convention stating the usecase of the newly added url?

It can be used by other connectors for other use cases in future so it is better to be like that

pub cancellation_policy: Option<Vec<u8>>,
pub cancellation_policy_type: Option<String>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub cancellation_policy_type: Option<String>,
pub cancellation_policy_file_type: Option<String>,

pub cancellation_policy: Option<Vec<u8>>,
pub cancellation_policy_type: Option<String>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Please follow the same convention for all the other newly added fields

@@ -52,16 +50,18 @@ impl Adyen {
}
}
}

fn get_key(auth_type: &types::ConnectorAuthType) -> CustomResult<String, errors::ConnectorError> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this to transformers and name it more meaningful?

) -> CustomResult<types::AcceptDisputeRouterData, errors::ConnectorError> {
Ok(types::AcceptDisputeRouterData {
response: Ok(types::AcceptDisputeResponse {
dispute_status: api::enums::DisputeStatus::DisputeAccepted,
Copy link
Contributor

Choose a reason for hiding this comment

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

We can't hardcode the status here, when we get a 200 response.

Instead, we need to consume the body and decide the status

{
  "disputeServiceResult": {
    "success": true
  }
}

) -> CustomResult<types::SubmitEvidenceRouterData, errors::ConnectorError> {
Ok(types::SubmitEvidenceRouterData {
response: Ok(types::SubmitEvidenceResponse {
dispute_status: api_models::enums::DisputeStatus::DisputeChallenged,
Copy link
Contributor

Choose a reason for hiding this comment

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

Please consume the response body and decide the status

}
#[async_trait::async_trait]
impl api::FileUpload for Adyen {
fn validate_file_upload(
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok(Self {
dispute_psp_reference: item.request.connector_dispute_id.clone(),
merchant_account_code,
defense_reason_code: "SupplyDefenseMaterial".into(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please attach the screenshot of adyen support confirming the usage of SupplyDefenseMaterial in all Defend dispute requests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image

Copy link
Contributor

Choose a reason for hiding this comment

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

What about the production environment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, We can use this for production also

defense_documents.push(DefenseDocuments {
content: get_content(service_documentation),
content_type: item.service_documentation_type,
defense_document_type_code: "DefenseMaterial".into(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we take this as an input from the merchant, or is it okay to hardcode it to be DefenseMaterial?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is okay to be DefenceMaterial

Copy link
Contributor

@sai-harsha-vardhan sai-harsha-vardhan Aug 9, 2024

Choose a reason for hiding this comment

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

Did we get a confirmation for this from adyen for production environment? : Yes We got that
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes We can use that

@@ -175,9 +175,9 @@ pub async fn retrieve_file_and_provider_file_id_from_file_id(
merchant_account: &domain::MerchantAccount,
key_store: &domain::MerchantKeyStore,
is_connector_file_data_required: api::FileDataRequired,
) -> CustomResult<(Option<Vec<u8>>, Option<String>), errors::ApiErrorResponse> {
) -> CustomResult<(Option<Vec<u8>>, Option<String>, Option<String>), errors::ApiErrorResponse> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of returning a tuple with 3 elements, please create a struct for this

@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from 7d55c76 to 635ca1c Compare August 7, 2024 21:27
@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from 1e839ad to 86080df Compare August 8, 2024 09:57
@@ -49,6 +49,11 @@ pub struct DisputeResponse {
pub merchant_connector_id: Option<String>,
}

pub struct FileInfo {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this struct defined in api_models? We can consider this moving into hyperswitch domain models

pub adyenplatform: ConnectorParams,
#[cfg(not(feature = "payouts"))]
pub adyen: ConnectorParams,
pub adyen: ConnectorParamsWithThreeBaseUrls,
Copy link
Contributor

Choose a reason for hiding this comment

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

Previously this didn't have a secondary_base_url, why are we adding it now?

.response
.parse_struct("AdyenDisputeResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
if response.success {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this block to transformers please?

.response
.parse_struct("AdyenDisputeResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
if response.success {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this block to transformers please?

.parse_struct("AdyenDisputeResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

if response.success {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move this block to transformers please and try to reuse the same function?

Ok(Self {
dispute_psp_reference: item.request.connector_dispute_id.clone(),
merchant_account_code,
defense_reason_code: "SupplyDefenseMaterial".into(),
Copy link
Contributor

Choose a reason for hiding this comment

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

What about the production environment?

defense_documents.push(DefenseDocuments {
content: get_content(service_documentation),
content_type: item.service_documentation_type,
defense_document_type_code: "DefenseMaterial".into(),
Copy link
Contributor

@sai-harsha-vardhan sai-harsha-vardhan Aug 9, 2024

Choose a reason for hiding this comment

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

Did we get a confirmation for this from adyen for production environment? : Yes We got that
image

@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Aug 11, 2024
@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from 759fe1a to 71853aa Compare August 12, 2024 04:40
@hyperswitch-bot hyperswitch-bot bot removed the M-api-contract-changes Metadata: This PR involves API contract changes label Aug 12, 2024
@@ -11,7 +11,7 @@ use serde::Deserialize;
pub struct Connectors {
pub aci: ConnectorParams,
#[cfg(feature = "payouts")]
pub adyen: ConnectorParamsWithSecondaryBaseUrl,
pub adyen: ConnectorParamsWithThreeBaseUrls,
pub adyenplatform: ConnectorParams,
#[cfg(not(feature = "payouts"))]
pub adyen: ConnectorParams,
Copy link
Contributor

Choose a reason for hiding this comment

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

Irrespective of payout feature being enabled, we would need this url for disputes right?

@sai-harsha-vardhan
Copy link
Contributor

CI checks are failing, please check @KiranKBR

@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from fa954c1 to 29611c2 Compare August 12, 2024 10:57
Copy link
Contributor

@sai-harsha-vardhan sai-harsha-vardhan left a comment

Choose a reason for hiding this comment

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

LGTM

@SamraatBansal SamraatBansal added A-connector-integration Area: Connector integration C-feature Category: Feature request or enhancement labels Aug 13, 2024
@SamraatBansal SamraatBansal added this to the August 2024 Release milestone Aug 13, 2024
Comment on lines 2220 to 2221
if (file_type.to_string().as_str() == "image/jpeg"
|| file_type.to_string().as_str() == "image/jpeg")
Copy link
Contributor

Choose a reason for hiding this comment

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

This OR clause is checking same thing, please updated with intended check

SamraatBansal
SamraatBansal previously approved these changes Aug 13, 2024
@jarnura
Copy link
Member

jarnura commented Aug 14, 2024

@sai-harsha-vardhan Should we make FileData's vec<u8> to Secret<vec<u8>>? I feel we should make them into secret.

@sai-harsha-vardhan
Copy link
Contributor

sai-harsha-vardhan commented Aug 14, 2024

Yes @jarnura, We can make File data as Secret<vec<u8>>. @KiranKBR Can you please make this change?

@KiranKBR
Copy link
Contributor Author

KiranKBR commented Aug 14, 2024

Yes @jarnura, We can make File data as Secret<vec<u8>>. @KiranKBR Can you please make this change?

We r are sending connector request with aggregated string of vec so we can make that string as Secret

Updae: struct has updated
image

@KiranKBR KiranKBR force-pushed the adyene-dispute-defend branch from a72e67d to 260046d Compare August 14, 2024 09:17
@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Aug 23, 2024
Merged via the queue into main with commit ad9f91b Aug 23, 2024
26 checks passed
@Gnanasundari24 Gnanasundari24 deleted the adyene-dispute-defend branch August 23, 2024 07:08
pixincreate added a commit that referenced this pull request Aug 23, 2024
* 'main' of github.com:juspay/hyperswitch:
  feat(connector): [Adyen] add dispute flows for adyen connector (#5514)
  chore(version): 2024.08.23.0
  feat(router): [cybersource] add disable_avs and disable_cvn flag in connector metadata  (#5667)
  feat(customer_v2): add route for customer retrieve v2  (#5516)
  chore(version): 2024.08.22.1
  fix(router): [Adyen]  prevent partial submission of billing address and add required fields for all payment methods (#5660)
  docs(README): Adding Contributors guide (#5184)
  Docs: Adding redirect url details (#5507)
  feat(global_id): create a `GlobalId` domain type (#5644)
  feat(router): collect customer address details based on business profile config regardless of connector required fields (#5418)
  feat: add new routes for profile level list apis (#5589)
  refactor(core):  Refactor fallback routing behaviour in payments for v2 (#5642)
  refactor(router): add connector_transaction_id, send response body and use admin_api_auth_with_merchant_id for payments manual update flow (#5658)
pixincreate added a commit that referenced this pull request Aug 27, 2024
* 'main' of github.com:juspay/hyperswitch: (134 commits)
  refactor(open_banking): Added merchant data update in mca update (#5655)
  feat: add test_mode for quickly testing payout links (#5669)
  refactor: introduce a domain type for profile ID (#5687)
  ci(cypress): update paybox configs (#5664)
  feat(openapi):  Add open api routes for routing v2 (#5686)
  feat(connector): [NOVALNET] Add template code (#5670)
  feat(user): business email update (#5674)
  chore(config): add production connector-configs for netcetera external 3ds flow (#5698)
  chore(version): 2024.08.27.0
  refactor(euclid): make the disabled node's relation as negative (#5701)
  feat: populate payment method details in payments response (#5661)
  build(deps): bump `diesel` to `2.2.3` and `sqlx` to `0.8.1` (#5688)
  feat(customer_v2):  added list customer v2 end point (#5517)
  feat(business_profile): add tax_connector_id column in business_profile table (#5576)
  chore: create v2 route for organization (#5679)
  refactor(payments_response): remove setter from payments response (#5676)
  feat(payment_methods_v2): Payment methods v2 API models (#5564)
  chore(version): 2024.08.26.0
  feat(connector): [Adyen] add dispute flows for adyen connector (#5514)
  chore(version): 2024.08.23.0
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-connector-integration Area: Connector integration C-feature Category: Feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants