hyperswitch icon indicating copy to clipboard operation
hyperswitch copied to clipboard

feat(FRM): Revise post FRM core flows

Open srujanchikke opened this issue 10 months ago • 0 comments

Type of Change

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

Description

PostAuth flow

  • The underlying FRM connector is invoked post authentication of the user from the bank.
  • If a transaction’s capture method was set to automatic, it is updated to manual for avoiding automatic capture.
  • Once the user completes the authentication, the underlying FRM connector is invoked. The decision whether or not to proceed with the txn is based on the status / score returned.

Possible actions based on the status

  • Continue on Accept - Continue with the transaction
  • Halt on Decline - Mark the transaction as cancelled
  • Approve / Decline on Manual Review - Hold the txn in manual review state. Merchants can list and review such transactions. - If approved, payment is captured - If declined, payment is voided

This flow works same as payments flow incase of 3ds, non 3ds, manual and auto capture for Legit 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?

Create merchant connector account with stripe and then create new merchant connector account with signifyd.

{
  "connector_type": "payment_VAS",
  "connector_name": "signifyd",
  "connector_account_details": {
    "auth_type": "HeaderKey",
    "api_key": "YOUR_API_KEY"
  },
  "test_mode": false,
  "disabled": false,
  "business_country": "US",
  "business_label": "default",
  "payment_methods_enabled": [
    {
      "payment_method": "card",
      "payment_method_types": [
        {
          "payment_method_type": "credit",
          "card_networks": [
            "Visa",
            "Mastercard"
          ],
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        },
        {
          "payment_method_type": "debit",
          "card_networks": [
            "Visa",
            "Mastercard"
          ],
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        }
      ]
    },
    {
      "payment_method": "pay_later",
      "payment_method_types": [
        {
          "payment_method_type": "klarna",
          "payment_experience": "redirect_to_url",
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        },
        {
          "payment_method_type": "affirm",
          "payment_experience": "redirect_to_url",
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        },
        {
          "payment_method_type": "afterpay_clearpay",
          "payment_experience": "redirect_to_url",
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        }
      ]
    }
  ],
  "metadata": {
    "city": "NY",
    "unit": "245"
  },
  "frm_configs": [
        {
            "gateway": "stripe",
            "payment_methods": [
                {
                    "payment_method": "card",
                    "payment_method_types": [
                        {
                            "payment_method_type": "credit",
                            "card_networks": [
                                "Visa"
                            ],
                            "flow": "post",
                            "action": "cancel_txn"
                        },
                        {
                            "payment_method_type": "debit",
                            "card_networks": [
                                "Visa"
                            ],
                            "flow": "post",
                            "action": "manual_review"
                        }
                    ]
                }
            ]
        }
    ]
}

TEST CASE 1 : Create legit card payment(amount =150) for both credit and debit. This should go as normal payment flow. Interchange CaptureMethod and CardType(3ds, non 3ds). TEST CASE 2 : Create Credit(pmt type) Payment with amount equals to 15000. This should Fail the payment .

{
  "amount": 15000,
  "currency": "USD",
  "confirm": true,
  "capture_method": "manual",
  "capture_on": "2022-09-10T10:11:12Z",
  "amount_to_capture": 1500,
  "customer_id": "StripeCustomer2",
  "email": "[email protected]",
  "name": "Bob Smith",
  "phone": "999999999",
  "phone_country_code": "+91",
  "description": "Its my first payment request",
  "authentication_type": "no_three_ds",
  "return_url": "https://google.com",
  "payment_method": "card",
  "payment_method_type": "credit",
  "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": "joseph",
      "last_name": "Doe"
    },
    "phone": {
      "number": "8056594427",
      "country_code": "+91"
    }
  },
  "shipping": {
    "address": {
      "line1": "1467",
      "line2": "31 Sherwood Gardens",
      "line3": "31 Sherwood Gardens",
      "city": "London",
      "state": "Manchester",
      "zip": "E14 9wn",
      "country": "GB",
      "first_name": "Bob",
      "last_name": "Smith"
    },
    "phone": {
      "number": "8056594427",
      "country_code": "+91"
    }
  },
  "statement_descriptor_name": "joseph",
  "statement_descriptor_suffix": "JS",
  "metadata": {
    "udf1": "value1",
    "new_customer": "true",
    "login_date": "2019-09-10T10:11:12Z"
  },
  "order_details" : [{
        "product_name" : "gillete creme",
        "quantity" : 2,
        "amount" : 600
    },
    {
        "product_name" : "gillete razor",
        "quantity" : 1,
        "amount" : 300
    }]
}

TESTCASE 3 : Create a payment with payment method type as debit. set capture method to automatic and use same json above. Payment status should be moved to RequiresMerchantAction. Response looks like this

{
    "payment_id": "pay_9ZgUMXOiLv35wrF6oOyd",
    "merchant_id": "merchant_1713525778",
    "status": "requires_merchant_action",
    "amount": 150000,
    "net_amount": 150000,
    "amount_capturable": 150000,
    "amount_received": 0,
    "connector": "stripe",
    "client_secret": "pay_9ZgUMXOiLv35wrF6oOyd_secret_wU5hfAMsYjBe0fXepJmj",
    "created": "2024-04-19T11:58:40.749Z",
    "currency": "USD",
    "customer_id": "StripeCustomer2",
    "customer": {
        "id": "StripeCustomer2",
        "name": "Bob Smith",
        "email": "[email protected]",
        "phone": "999999999",
        "phone_country_code": "+91"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "manual",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4242",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "424242",
            "card_extended_bin": "42424242",
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": {
                "cvc_check": "pass",
                "address_line1_check": "pass",
                "address_postal_code_check": "pass"
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "London",
            "country": "GB",
            "line1": "1467",
            "line2": "31 Sherwood Gardens",
            "line3": "31 Sherwood Gardens",
            "zip": "E14 9wn",
            "state": "Manchester",
            "first_name": "Bob",
            "last_name": "Smith"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": [
        {
            "brand": null,
            "amount": 600,
            "category": null,
            "quantity": 2,
            "product_id": null,
            "product_name": "gillete creme",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        },
        {
            "brand": null,
            "amount": 300,
            "category": null,
            "quantity": 1,
            "product_id": null,
            "product_name": "gillete razor",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        }
    ],
    "email": "[email protected]",
    "name": "Bob Smith",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": "fraud",
    "error_message": "Refund Initiated with the processor",
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "StripeCustomer2",
        "created_at": 1713527920,
        "expires": 1713531520,
        "secret": "epk_22d24f1ef64b44a0b28b72a81c2b2392"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "frm_message": {
        "frm_name": "signifyd",
        "frm_transaction_id": "pay_9ZgUMXOiLv35wrF6oOyd_1",
        "frm_transaction_type": "post_frm",
        "frm_status": "fraud",
        "frm_score": 497,
        "frm_reason": "SIGNIFYD_DECLINED",
        "frm_error": null
    },
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "payment_link": null,
    "profile_id": "pro_DOE6fPp88zY1M4xUyO0H",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_U0XVEDfTM0JMpiv9GOr5",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-19T12:13:40.749Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-04-19T11:58:41.929Z"
}

/approve and /reject should hit after above step for the same payment which will mark payment as succeeded and failed by merchant.

Request curl :

curl --location --request POST 'http://localhost:8080/payments/{{payment_id}}/approve' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:{{api_key}}' \
--data-raw '{}'

Response json :

{
    "payment_id": "pay_9ZgUMXOiLv35wrF6oOyd",
    "merchant_id": "merchant_1713525778",
    "status": "succeeded",
    "amount": 150000,
    "net_amount": 150000,
    "amount_capturable": 0,
    "amount_received": 150000,
    "connector": "stripe",
    "client_secret": "pay_9ZgUMXOiLv35wrF6oOyd_secret_wU5hfAMsYjBe0fXepJmj",
    "created": "2024-04-19T11:58:40.749Z",
    "currency": "USD",
    "customer_id": "StripeCustomer2",
    "customer": {
        "id": "StripeCustomer2",
        "name": "Bob Smith",
        "email": "[email protected]",
        "phone": "999999999",
        "phone_country_code": "+91"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "manual",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4242",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "424242",
            "card_extended_bin": "42424242",
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": {
                "cvc_check": "pass",
                "address_line1_check": "pass",
                "address_postal_code_check": "pass"
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "London",
            "country": "GB",
            "line1": "1467",
            "line2": "31 Sherwood Gardens",
            "line3": "31 Sherwood Gardens",
            "zip": "E14 9wn",
            "state": "Manchester",
            "first_name": "Bob",
            "last_name": "Smith"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": [
        {
            "brand": null,
            "amount": 600,
            "category": null,
            "quantity": 2,
            "product_id": null,
            "product_name": "gillete creme",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        },
        {
            "brand": null,
            "amount": 300,
            "category": null,
            "quantity": 1,
            "product_id": null,
            "product_name": "gillete razor",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        }
    ],
    "email": "[email protected]",
    "name": "Bob Smith",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": false,
    "connector_transaction_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "frm_message": {
        "frm_name": "signifyd",
        "frm_transaction_id": "pay_9ZgUMXOiLv35wrF6oOyd_1",
        "frm_transaction_type": "post_frm",
        "frm_status": "fraud",
        "frm_score": 497,
        "frm_reason": "SIGNIFYD_DECLINED",
        "frm_error": null
    },
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "payment_link": null,
    "profile_id": "pro_DOE6fPp88zY1M4xUyO0H",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": "approved",
    "merchant_connector_id": "mca_U0XVEDfTM0JMpiv9GOr5",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-19T12:13:40.749Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-04-19T12:01:50.944Z"
}

Checklist

  • [x] I formatted the code cargo +nightly fmt --all
  • [x] I addressed lints thrown by cargo clippy
  • [x] I reviewed the submitted code
  • [ ] I added unit tests for my changes where possible
  • [ ] I added a CHANGELOG entry if applicable

srujanchikke avatar Apr 18 '24 11:04 srujanchikke