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(core): Add payments update-intent API for v2 #6490

Open
wants to merge 31 commits into
base: main
Choose a base branch
from

Conversation

AnuthaDev
Copy link
Contributor

@AnuthaDev AnuthaDev commented Nov 6, 2024

Type of Change

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

Description

Added update-intent API for payments

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?

  1. Intent Create Response:
{
    "id": "12345_pay_01930aca908b76b086927c61ad1fb724",
    "status": "requires_payment_method",
    "amount_details": {
        "order_amount": 100,
        "currency": "USD",
        "shipping_cost": null,
        "order_tax_amount": null,
        "skip_external_tax_calculation": "Skip",
        "skip_surcharge_calculation": "Skip",
        "surcharge_amount": null,
        "tax_on_surcharge": null
    },
    "client_secret": "12345_pay_01930aca908b76b086927c61ad1fb724_secret_01930aca908c71c3b6233716f1caede1",
    "merchant_reference_id": null,
    "routing_algorithm_id": null,
    "capture_method": "automatic",
    "authentication_type": "no_three_ds",
    "billing": null,
    "shipping": null,
    "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": "2024-11-08T08:16:07.724Z",
    "frm_metadata": null,
    "request_external_three_ds_authentication": "Skip"
}

2a. Update Intent Request

curl --location --request PUT 'http://localhost:8080/v2/payments/12345_pay_01930aca908b76b086927c61ad1fb724/update-intent' \
--header 'Content-Type: application/json' \
--header 'X-Profile-Id: pro_iRfuYPs5lF63PsQlFNfe' \
--header 'api-key: dev_KPgd8JjUH2jj4n20aYOOGRc9loG5b5w4d46yxOrNOpfpF3UrUuNDFm2tWepZJaEp' \
--data-raw '{
 "amount_details": {
    "order_amount": 6540,
    "currency": "AED",
    "shipping_cost": 123,
    "order_tax_amount": 123,
    "skip_external_tax_calculation": "Skip",
    "skip_surcharge_calculation": "Skip",
    "surcharge_amount": 123,
    "tax_on_surcharge": 123
  },

  "capture_method": "automatic",
  "authentication_type": "three_ds",
  "billing": {
    "address": {
      "city": "New York",
      "country": "AF",
      "line1": "123, King Street",
      "line2": "Powelson Avenue",
      "line3": "Bridgewater",
      "zip": "08807",
      "state": "New York",
      "first_name": "John",
      "last_name": "Doe"
    },
    "phone": {
      "number": "9123456789",
      "country_code": "+1"
    },
    "email": "[email protected]"
  },
  "shipping": {
    "address": {
      "city": "New York",
      "country": "AF",
      "line1": "123, King Street",
      "line2": "Powelson Avenue",
      "line3": "Bridgewater",
      "zip": "08807",
      "state": "New York",
      "first_name": "John",
      "last_name": "Doe"
    },
    "phone": {
      "number": "9123456789",
      "country_code": "+1"
    },
    "email": "[email protected]"
  },
  "customer_present": "Present",
  "description": "It'\''s my first payment request",
  "return_url": "https://hyperswitch.io",
  "setup_future_usage": "off_session",
  "apply_mit_exemption": "Apply",
  "statement_descriptor": "Hyperswitch Router",
  "order_details": [{
    "product_name": "Apple iPhone 16",
    "quantity": 1,
    "amount" : 69000,
    "product_img_link": "https://dummy-img-link.com"
  }],
  "allowed_payment_method_types": [
    "ach"
  ],
  "metadata": {},
  "connector_metadata": {
    "apple_pay": {
      "session_token_data": {
        "payment_processing_certificate": "<string>",
        "payment_processing_certificate_key": "<string>",
        "payment_processing_details_at": "Hyperswitch",
        "certificate": "<string>",
        "certificate_keys": "<string>",
        "merchant_identifier": "<string>",
        "display_name": "<string>",
        "initiative": "web",
        "initiative_context": "<string>",
        "merchant_business_country": "AF"
      }
    },
    "airwallex": {
      "payload": "<string>"
    },
    "noon": {
      "order_category": "<string>"
    }
  },
  "feature_metadata": {
    "redirect_response": {
      "param": "<string>",
      "json_payload": {}
    },
    "search_tags": [
      "<string>"
    ]
  },
  "enable_payment_link": "Enable",
  "payment_link_config": {
    "theme": "#4E6ADD",
    "logo": "https://i.pinimg.com/736x/4d/83/5c/4d835ca8aafbbb15f84d07d926fda473.jpg",
    "seller_name": "hyperswitch",
    "sdk_layout": "accordion",
    "display_sdk_only": true,
    "enabled_saved_payment_method": true,
    "transaction_details": [
      {
        "key": "Policy-Number",
        "value": "297472368473924",
        "ui_configuration": {
          "position": 5,
          "is_key_bold": true,
          "is_value_bold": true
        }
      }
    ]
  },
  "request_incremental_authorization": "true",
  "session_expiry": 1000,
  "frm_metadata": {},
  "request_external_three_ds_authentication": "Enable"
    
}'

2b. Update Intent Response

{
    "id": "12345_pay_01930aca908b76b086927c61ad1fb724",
    "status": "requires_payment_method",
    "amount_details": {
        "order_amount": 6540,
        "currency": "AED",
        "shipping_cost": 123,
        "order_tax_amount": null,
        "skip_external_tax_calculation": "Calculate",
        "skip_surcharge_calculation": "Calculate",
        "surcharge_amount": 123,
        "tax_on_surcharge": 123
    },
    "client_secret": "12345_pay_01930aca908b76b086927c61ad1fb724_secret_01930aca908c71c3b6233716f1caede1",
    "merchant_reference_id": null,
    "routing_algorithm_id": null,
    "capture_method": "automatic",
    "authentication_type": "three_ds",
    "billing": {
        "address": {
            "city": "New York",
            "country": "AF",
            "line1": "123, King Street",
            "line2": "Powelson Avenue",
            "line3": "Bridgewater",
            "zip": "08807",
            "state": "New York",
            "first_name": "John",
            "last_name": "Doe"
        },
        "phone": {
            "number": "9123456789",
            "country_code": "+1"
        },
        "email": "[email protected]"
    },
    "shipping": {
        "address": {
            "city": "New York",
            "country": "AF",
            "line1": "123, King Street",
            "line2": "Powelson Avenue",
            "line3": "Bridgewater",
            "zip": "08807",
            "state": "New York",
            "first_name": "John",
            "last_name": "Doe"
        },
        "phone": {
            "number": "9123456789",
            "country_code": "+1"
        },
        "email": "[email protected]"
    },
    "customer_id": null,
    "customer_present": "Present",
    "description": "It's my first payment request",
    "return_url": "https://hyperswitch.io/",
    "setup_future_usage": "off_session",
    "apply_mit_exemption": "Apply",
    "statement_descriptor": "Hyperswitch Router",
    "order_details": [
        {
            "product_name": "Apple iPhone 16",
            "quantity": 1,
            "amount": 69000,
            "requires_shipping": null,
            "product_img_link": "https://dummy-img-link.com",
            "product_id": null,
            "category": null,
            "sub_category": null,
            "brand": null,
            "product_type": null,
            "product_tax_code": null
        }
    ],
    "allowed_payment_method_types": [
        "ach"
    ],
    "metadata": {},
    "connector_metadata": {
        "apple_pay": {
            "session_token_data": {
                "payment_processing_certificate": "<string>",
                "payment_processing_certificate_key": "<string>",
                "payment_processing_details_at": "Hyperswitch",
                "certificate": "<string>",
                "certificate_keys": "<string>",
                "merchant_identifier": "<string>",
                "display_name": "<string>",
                "initiative": "web",
                "initiative_context": "<string>",
                "merchant_business_country": "AF"
            }
        },
        "airwallex": {
            "payload": "<string>"
        },
        "noon": {
            "order_category": "<string>"
        }
    },
    "feature_metadata": {
        "redirect_response": {
            "param": "<string>",
            "json_payload": {}
        },
        "search_tags": [
            "<string>"
        ]
    },
    "payment_link_enabled": "Enable",
    "payment_link_config": null,
    "request_incremental_authorization": "true",
    "expires_on": "2024-11-08T08:17:52.864Z",
    "frm_metadata": {},
    "request_external_three_ds_authentication": "Enable"
}

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

Copy link

semanticdiff-com bot commented Nov 6, 2024

Review changes with  SemanticDiff

Changed Files
File Status
  crates/router/src/types/api/payments.rs  89% smaller
  crates/diesel_models/src/query/payment_attempt.rs  85% smaller
  crates/router/src/services/authentication.rs  62% smaller
  crates/router/src/core/payments/operations/payment_create_intent.rs  52% smaller
  crates/router/src/core/payments/operations/payment_get_intent.rs  52% smaller
  crates/router/src/routes/app.rs  41% smaller
  crates/diesel_models/src/query/payment_intent.rs  40% smaller
  crates/router/src/core/payments/operations/payment_response.rs  38% smaller
  crates/router/src/routes/lock_utils.rs  35% smaller
  crates/router/src/core/payments/operations/payment_confirm_intent.rs  23% smaller
  crates/openapi/src/openapi_v2.rs  14% smaller
  crates/common_enums/src/transformers.rs  14% smaller
  crates/router/src/core/payments.rs  14% smaller
  crates/storage_impl/src/payments/payment_intent.rs  13% smaller
  crates/hyperswitch_domain_models/src/payments/payment_intent.rs  8% smaller
  crates/router/src/core/payments/operations.rs  5% smaller
  crates/router/src/types/transformers.rs  3% smaller
  crates/storage_impl/src/mock_db/payment_attempt.rs  2% smaller
  crates/api_models/src/events/payment.rs  1% smaller
  crates/diesel_models/src/payment_intent.rs  1% smaller
  api-reference-v2/api-reference/payments/payments--update-intent.mdx Unsupported file format
  api-reference-v2/mint.json  0% smaller
  api-reference-v2/openapi_spec.json  0% smaller
  crates/api_models/src/payments.rs  0% smaller
  crates/common_enums/src/enums.rs  0% smaller
  crates/diesel_models/src/kv.rs  0% smaller
  crates/hyperswitch_domain_models/src/merchant_connector_account.rs  0% smaller
  crates/hyperswitch_domain_models/src/payments.rs  0% smaller
  crates/hyperswitch_domain_models/src/payments/payment_attempt.rs  0% smaller
  crates/hyperswitch_domain_models/src/router_data.rs  0% smaller
  crates/hyperswitch_domain_models/src/router_flow_types/payments.rs  0% smaller
  crates/openapi/src/routes/payments.rs  0% smaller
  crates/router/src/compatibility/stripe/payment_intents.rs  0% smaller
  crates/router/src/compatibility/stripe/setup_intents.rs  0% smaller
  crates/router/src/core/authentication/utils.rs Unsupported file format
  crates/router/src/core/fraud_check/flows/checkout_flow.rs  0% smaller
  crates/router/src/core/fraud_check/flows/record_return.rs  0% smaller
  crates/router/src/core/fraud_check/flows/sale_flow.rs  0% smaller
  crates/router/src/core/fraud_check/flows/transaction_flow.rs  0% smaller
  crates/router/src/core/payments/flows.rs  0% smaller
  crates/router/src/core/payments/flows/approve_flow.rs  0% smaller
  crates/router/src/core/payments/flows/authorize_flow.rs  0% smaller
  crates/router/src/core/payments/flows/cancel_flow.rs  0% smaller
  crates/router/src/core/payments/flows/capture_flow.rs  0% smaller
  crates/router/src/core/payments/flows/complete_authorize_flow.rs  0% smaller
  crates/router/src/core/payments/flows/incremental_authorization_flow.rs  0% smaller
  crates/router/src/core/payments/flows/post_session_tokens_flow.rs  0% smaller
  crates/router/src/core/payments/flows/psync_flow.rs  0% smaller
  crates/router/src/core/payments/flows/reject_flow.rs  0% smaller
  crates/router/src/core/payments/flows/session_flow.rs  0% smaller
  crates/router/src/core/payments/flows/session_update_flow.rs  0% smaller
  crates/router/src/core/payments/flows/setup_mandate_flow.rs  0% smaller
  crates/router/src/core/payments/operations/payment_get.rs  0% smaller
  crates/router/src/core/payments/operations/payment_update_intent.rs  0% smaller
  crates/router/src/core/payments/retry.rs  0% smaller
  crates/router/src/core/payments/transformers.rs  0% smaller
  crates/router/src/core/payouts.rs  0% smaller
  crates/router/src/db/kafka_store.rs  0% smaller
  crates/router/src/routes/payments.rs  0% smaller
  crates/router/src/types/api.rs  0% smaller
  crates/router_env/src/logger/types.rs  0% smaller
  crates/storage_impl/src/mock_db/payment_intent.rs  0% smaller
  crates/storage_impl/src/payments/payment_attempt.rs  0% smaller
  v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/down.sql Unsupported file format
  v2_migrations/2024-08-28-081838_update_v2_primary_key_constraints/up.sql Unsupported file format

@hyperswitch-bot hyperswitch-bot bot added M-database-changes Metadata: This PR involves database schema changes M-api-contract-changes Metadata: This PR involves API contract changes labels Nov 6, 2024
@AnuthaDev AnuthaDev changed the base branch from main to payment_sync_v2 November 6, 2024 09:27
@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Nov 6, 2024
@AnuthaDev AnuthaDev marked this pull request as ready for review November 7, 2024 08:38
@AnuthaDev AnuthaDev requested review from a team as code owners November 7, 2024 08:38
@AnuthaDev AnuthaDev added the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Nov 7, 2024
crates/api_models/src/payments.rs Outdated Show resolved Hide resolved
crates/diesel_models/src/payment_intent.rs Outdated Show resolved Hide resolved
#[diesel(column_name = enable_payment_link)]
pub payment_link_enabled: Option<bool>,
// TODO: Check this type
// pub payment_link_config: Option<PaymentLinkConfigRequestForPayments>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

PaymentLinkConfigRequestForPayments conflicts with AsChangeSet. I'd appreciate some guidance :)

currency: Option<storage_enums::Currency>,
shipping_cost: Option<MinorUnit>,
// TODO: Check how to handle this
// tax_details: Option<diesel_models::TaxDetails>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I presume tax_details is not a supported field in update?

Copy link
Member

Choose a reason for hiding this comment

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

there is a default inside TaxDetails, we can allow that field for the update.

@@ -376,48 +390,194 @@ impl From<PaymentIntentUpdate> for diesel_models::PaymentIntentUpdateInternal {
status: Some(status),
active_attempt_id: Some(active_attempt_id),
modified_at: common_utils::date_time::now(),
amount: None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a way to avoid explicitly writing so many None s

payment_data,
customer.clone(),
merchant_account.storage_scheme,
None, //TODO: fix this
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this OK or do I need to provide proper values here?

Copy link
Member

Choose a reason for hiding this comment

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

We should not update customer related information in update-intent

@@ -7182,3 +7205,217 @@ impl<F: Clone> OperationSessionSetters<F> for PaymentStatusData<F> {
todo!()
}
}

#[cfg(feature = "v2")]
impl<F: Clone> OperationSessionGetters<F> for PaymentUpdateData<F> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a way to avoid this unnecessary boilerplate?

Copy link
Member

Choose a reason for hiding this comment

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

After all flows where done, we need to remove unnecessary boilerplate, until that we need to keep it.

.map(|order_details| order_details.into_iter().map(Secret::new).collect());

// TODO: This should most likely be created_time + session_expiry rather than now + session_expiry
let session_expiry = request.session_expiry.map(|expiry| {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please suggest the best course of action here.

Copy link
Member

Choose a reason for hiding this comment

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

Make the logic on created_time, so that it gives a meaning like payments will expire after 15mins or some x mins from created time. If its now, we can't able to have a clear picture like that.

),
errors::StorageError,
> {
Ok((Box::new(self), None))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do I need to implement this as well?

Copy link
Member

Choose a reason for hiding this comment

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

No

auth.key_store,
payments::operations::PaymentUpdateIntent,
req.payload,
Some(global_payment_id.clone()),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a way to avoid cloning here? I get an error because of the closure

#[schema(value_type = Option<String>)]
pub routing_algorithm_id: Option<id_type::RoutingId>,

#[schema(value_type = Option<CaptureMethod>, example = "automatic")]
Copy link
Contributor Author

@AnuthaDev AnuthaDev Nov 7, 2024

Choose a reason for hiding this comment

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

Should the value_type be omitted for fields having same type as value_type?

Copy link
Member

Choose a reason for hiding this comment

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

No, we are specifying the value type here because the enum is type qualified. Type qualified usages do not work with ToSchema macro

crates/api_models/src/payments.rs Outdated Show resolved Hide resolved
crates/api_models/src/payments.rs Outdated Show resolved Hide resolved
crates/api_models/src/payments.rs Outdated Show resolved Hide resolved
crates/diesel_models/src/payment_intent.rs Outdated Show resolved Hide resolved
{
pub flow: PhantomData<F>,
pub payment_intent: PaymentIntent,
pub payment_intent_update: PaymentIntentUpdate,
Copy link
Member

Choose a reason for hiding this comment

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

this object is not required, in PaymentsState. Is this necessary to be placed in state?

crates/openapi/src/routes/payments.rs Outdated Show resolved Hide resolved
crates/router/src/core/payments.rs Show resolved Hide resolved
@@ -7182,3 +7205,217 @@ impl<F: Clone> OperationSessionSetters<F> for PaymentStatusData<F> {
todo!()
}
}

#[cfg(feature = "v2")]
impl<F: Clone> OperationSessionGetters<F> for PaymentUpdateData<F> {
Copy link
Member

Choose a reason for hiding this comment

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

After all flows where done, we need to remove unnecessary boilerplate, until that we need to keep it.

.map(|order_details| order_details.into_iter().map(Secret::new).collect());

// TODO: This should most likely be created_time + session_expiry rather than now + session_expiry
let session_expiry = request.session_expiry.map(|expiry| {
Copy link
Member

Choose a reason for hiding this comment

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

Make the logic on created_time, so that it gives a meaning like payments will expire after 15mins or some x mins from created time. If its now, we can't able to have a clear picture like that.

payment_link_enabled: request.payment_link_enabled.clone(),
request_incremental_authorization: request.request_incremental_authorization,
session_expiry,
// TODO: Does frm_metadata need more processing?
Copy link
Member

Choose a reason for hiding this comment

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

Remove this todo, we don't need any processing for frm with current feature set.

currency: Option<storage_enums::Currency>,
shipping_cost: Option<MinorUnit>,
// TODO: Check how to handle this
// tax_details: Option<diesel_models::TaxDetails>,
Copy link
Member

Choose a reason for hiding this comment

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

there is a default inside TaxDetails, we can allow that field for the update.

payment_data,
customer.clone(),
merchant_account.storage_scheme,
None, //TODO: fix this
Copy link
Member

Choose a reason for hiding this comment

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

We should not update customer related information in update-intent

),
errors::StorageError,
> {
Ok((Box::new(self), None))
Copy link
Member

Choose a reason for hiding this comment

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

No

Base automatically changed from payment_sync_v2 to main November 11, 2024 14:06
@AnuthaDev AnuthaDev requested review from a team as code owners November 11, 2024 14:06
@hyperswitch-bot hyperswitch-bot bot added the M-database-changes Metadata: This PR involves database schema changes label Nov 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-v2 M-api-contract-changes Metadata: This PR involves API contract changes M-database-changes Metadata: This PR involves database schema changes S-waiting-on-review Status: This PR has been implemented and needs to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Add payments update-intent API for v2
3 participants