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(charges): integrated PaymentSync for stripe connect #4771

Merged
merged 14 commits into from
Sep 26, 2024

Conversation

kashif-m
Copy link
Contributor

@kashif-m kashif-m commented May 27, 2024

Type of Change

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

Description

This PR adds the charges sync capability (for PaymentSync and RefundSync) for Stripe 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

PaymentSync operation makes a get payment intent call to the underlying connector for fetching the payment details. This is used in HyperSwitch for updating the details of the payment stored at HyperSwitch's end. Capability for fetching charged payments for Stripe needs to be added in case a charged payment was made. This was earlier missed in this PR - #4628

How did you test it?

1. Create a 3DS txn

cURL

curl --location 'http://localhost:8080/payments' \
    --header 'Content-Type: application/json' \
    --header 'Accept: application/json' \
    --header 'api-key: dev_fU9hJ1iOCFFN1e5Ao0ikNCnh7GLlNd5GCtW7NHBP1vylXVcKax4BZhelhWCiOOFO' \
    --data '{
        "amount": 200,
        "profile_id": "pro_UyFz4kRsR7cF07w3ABgy",
        "currency": "USD",
        "confirm": true,
        "capture_method": "automatic",
        "amount_to_capture": 200,
        "customer_id": "cust_no_pm",
        "authentication_type": "three_ds",
        "return_url": "https://google.com",
        "name": "John Doe",
        "phone": "999999999",
        "phone_country_code": "+65",
        "description": "Its my first payment request",
        "payment_method": "card",
        "payment_method_type": "debit",
        "payment_method_data": {
            "card": {
                "card_number": "4242424242424242",
                "card_exp_month": "12",
                "card_exp_year": "34",
                "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"
            }
        },
        "charges": {
            "charge_type": "direct",
            "fees": 100,
            "transfer_account_id": "acct_1PDftAIhl7EEkW0O"
        }
    }'

Response

{
    "payment_id": "pay_WuYe8INincbthQtTnOdF",
    "merchant_id": "merchant_1726221242",
    "status": "requires_customer_action",
    "amount": 200,
    "net_amount": 200,
    "amount_capturable": 200,
    "amount_received": 0,
    "connector": "stripe",
    "client_secret": "pay_WuYe8INincbthQtTnOdF_secret_QCiiNSTdanW4BD6lVJ0q",
    "created": "2024-09-13T10:03:35.978Z",
    "currency": "USD",
    "customer_id": "cust_no_pm",
    "customer": {
        "id": "cust_no_pm",
        "name": "John Doe",
        "email": null,
        "phone": "999999999",
        "phone_country_code": "+65"
    },
    "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": "automatic",
    "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": null,
            "card_exp_month": "12",
            "card_exp_year": "34",
            "card_holder_name": null,
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": 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": null,
    "email": null,
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": {
        "type": "redirect_to_url",
        "redirect_to_url": "http://localhost:8080/payments/redirect/pay_WuYe8INincbthQtTnOdF/merchant_1726221242/pay_WuYe8INincbthQtTnOdF_1"
    },
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "debit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cust_no_pm",
        "created_at": 1726221815,
        "expires": 1726225415,
        "secret": "epk_f0aafd3930044592b00937e2ea564e8f"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": "pi_3PyWLkIhl7EEkW0O2rDLaqN6",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pi_3PyWLkIhl7EEkW0O2rDLaqN6",
    "payment_link": null,
    "profile_id": "pro_UyFz4kRsR7cF07w3ABgy",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_hUygDAQSZcNuwsf0gy7O",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-09-13T10:18:35.978Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-09-13T10:03:36.837Z",
    "charges": {
        "charge_id": null,
        "charge_type": "direct",
        "application_fees": 100,
        "transfer_account_id": "acct_1PDftAIhl7EEkW0O"
    },
    "frm_metadata": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null
}
  1. Open the 3DS link and complete the txn
3. Fetch payment details

cURL

curl --location 'http://localhost:8080/payments/pay_WuYe8INincbthQtTnOdF' \
--header 'Accept: application/json' \
--header 'api-key: dev_fU9hJ1iOCFFN1e5Ao0ikNCnh7GLlNd5GCtW7NHBP1vylXVcKax4BZhelhWCiOOFO'

Response

{
    "payment_id": "pay_WuYe8INincbthQtTnOdF",
    "merchant_id": "merchant_1726221242",
    "status": "succeeded",
    "amount": 200,
    "net_amount": 200,
    "amount_capturable": 0,
    "amount_received": 200,
    "connector": "stripe",
    "client_secret": "pay_WuYe8INincbthQtTnOdF_secret_QCiiNSTdanW4BD6lVJ0q",
    "created": "2024-09-13T10:03:35.978Z",
    "currency": "USD",
    "customer_id": "cust_no_pm",
    "customer": {
        "id": "cust_no_pm",
        "name": "John Doe",
        "email": null,
        "phone": "999999999",
        "phone_country_code": "+65"
    },
    "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": "automatic",
    "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": null,
            "card_exp_month": "12",
            "card_exp_year": "34",
            "card_holder_name": null,
            "payment_checks": {
                "cvc_check": "pass",
                "address_line1_check": "pass",
                "address_postal_code_check": "pass"
            },
            "authentication_data": {
                "result": "attempt_acknowledged",
                "version": "2.1.0",
                "result_reason": null,
                "transaction_id": "aef2622a-773b-4982-9fa0-8463cb92a48b",
                "authentication_flow": null,
                "exemption_indicator": null,
                "electronic_commerce_indicator": "06"
            }
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": 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": null,
    "email": null,
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "debit",
    "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_3PyWLkIhl7EEkW0O2rDLaqN6",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pi_3PyWLkIhl7EEkW0O2rDLaqN6",
    "payment_link": null,
    "profile_id": "pro_UyFz4kRsR7cF07w3ABgy",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_hUygDAQSZcNuwsf0gy7O",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-09-13T10:18:35.978Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-09-13T10:04:51.217Z",
    "charges": {
        "charge_id": "ch_3PyWLkIhl7EEkW0O2JuGIbKx",
        "charge_type": "direct",
        "application_fees": 100,
        "transfer_account_id": "acct_1PDftAIhl7EEkW0O"
    },
    "frm_metadata": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null
}
4. Create a refund

cURL

curl --location 'http://localhost:8080/refunds' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_fU9hJ1iOCFFN1e5Ao0ikNCnh7GLlNd5GCtW7NHBP1vylXVcKax4BZhelhWCiOOFO' \
--data '{
    "payment_id": "pay_WuYe8INincbthQtTnOdF",
    "amount": 100,
    "reason": "Customer returned product",
    "refund_type": "instant",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2024-05-05T10:11:12Z"
    },
    "charges": {
        "charge_id": "ch_3PyWLkIhl7EEkW0O2JuGIbKx",
        "revert_platform_fee": true
    }
}'

Response

{
    "refund_id": "ref_iHNDw2w1crHCanfz9ups",
    "payment_id": "pay_WuYe8INincbthQtTnOdF",
    "amount": 100,
    "currency": "USD",
    "status": "succeeded",
    "reason": "Customer returned product",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2024-05-05T10:11:12Z"
    },
    "error_message": null,
    "error_code": null,
    "created_at": "2024-09-13T10:05:46.911Z",
    "updated_at": "2024-09-13T10:05:48.284Z",
    "connector": "stripe",
    "profile_id": "pro_UyFz4kRsR7cF07w3ABgy",
    "merchant_connector_id": "mca_hUygDAQSZcNuwsf0gy7O",
    "charges": {
        "charge_id": "ch_3PyWLkIhl7EEkW0O2JuGIbKx",
        "revert_platform_fee": true,
        "revert_transfer": null
    }
}
5. Sync the charged refund

cURL

curl --location 'http://localhost:8080/refunds/sync' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'api-key: dev_qjitjFb7HmmPae9FVYtkIkxg61y4HbDOPXpjFhgHpvFGFAeb5TMoZyJE65jtBvoQ' \
--data '{
    "refund_id": "ref_N9LQM1JeKldMVb36ATm4",
    "force_sync": true
}'

Response (should not contain any error_code or error_message)

{
    "refund_id": "ref_N9LQM1JeKldMVb36ATm4",
    "payment_id": "pay_AqMMNRDPyg3W0vc7ZPE3",
    "amount": 1,
    "currency": "USD",
    "status": "succeeded",
    "reason": "Customer returned product",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2024-05-05T10:11:12Z"
    },
    "error_message": null,
    "error_code": null,
    "created_at": "2024-09-17T05:36:53.915Z",
    "updated_at": "2024-09-17T05:37:02.222Z",
    "connector": "stripe",
    "profile_id": "pro_DN1OG5zBAKvedfOXOwUp",
    "merchant_connector_id": "mca_P7ZEY0RUGhoj0uiqSFQx",
    "charges": {
        "charge_id": "ch_3Pztb7Ihl7EEkW0O2ugKycYU",
        "revert_platform_fee": true,
        "revert_transfer": null
    }
}
6. Retrieve the refund

cURL

curl --location 'http://localhost:8080/refunds/ref_N9LQM1JeKldMVb36ATm4' \
--header 'Accept: application/json' \
--header 'api-key: dev_qjitjFb7HmmPae9FVYtkIkxg61y4HbDOPXpjFhgHpvFGFAeb5TMoZyJE65jtBvoQ'

Response (should not contain any error_code or error_message)

{
    "refund_id": "ref_N9LQM1JeKldMVb36ATm4",
    "payment_id": "pay_AqMMNRDPyg3W0vc7ZPE3",
    "amount": 1,
    "currency": "USD",
    "status": "succeeded",
    "reason": "Customer returned product",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2024-05-05T10:11:12Z"
    },
    "error_message": null,
    "error_code": null,
    "created_at": "2024-09-17T05:36:53.915Z",
    "updated_at": "2024-09-17T05:37:02.222Z",
    "connector": "stripe",
    "profile_id": "pro_DN1OG5zBAKvedfOXOwUp",
    "merchant_connector_id": "mca_P7ZEY0RUGhoj0uiqSFQx",
    "charges": {
        "charge_id": "ch_3Pztb7Ihl7EEkW0O2ugKycYU",
        "revert_platform_fee": true,
        "revert_transfer": 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

@kashif-m kashif-m linked an issue May 27, 2024 that may be closed by this pull request
2 tasks
@kashif-m kashif-m marked this pull request as ready for review May 27, 2024 10:16
@kashif-m kashif-m requested review from a team as code owners May 27, 2024 10:16
@kashif-m kashif-m self-assigned this May 27, 2024
@kashif-m kashif-m added the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label May 27, 2024
}
}
api::Amount::Value(amount) => {
if charges.fees.get_amount_as_i64() > amount.into() {
Copy link
Contributor

@sai-harsha-vardhan sai-harsha-vardhan Sep 25, 2024

Choose a reason for hiding this comment

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

what if charges.fees is lesser than the amount, is it valid?

Copy link
Contributor Author

@kashif-m kashif-m Sep 25, 2024

Choose a reason for hiding this comment

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

fees being charged should always be lesser than the actual amount, that's the only* valid case

Copy link
Contributor

@deepanshu-iiitu deepanshu-iiitu left a comment

Choose a reason for hiding this comment

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

Approving on behalf of connectors

@likhinbopanna likhinbopanna added this pull request to the merge queue Sep 26, 2024
Merged via the queue into main with commit e0630a7 Sep 26, 2024
14 checks passed
@likhinbopanna likhinbopanna deleted the charges_sync branch September 26, 2024 07:42
@SanchithHegde SanchithHegde removed the S-waiting-on-review Status: This PR has been implemented and needs to be reviewed label Sep 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] add support for collecting and refunding charges on payments
6 participants