Skip to content

feat(connector): add amount conversion framework to Powertranz#9752

Merged
likhinbopanna merged 16 commits intojuspay:mainfrom
ARYPROGRAMMER:feat/powertranz-amount-conversion
Oct 16, 2025
Merged

feat(connector): add amount conversion framework to Powertranz#9752
likhinbopanna merged 16 commits intojuspay:mainfrom
ARYPROGRAMMER:feat/powertranz-amount-conversion

Conversation

@ARYPROGRAMMER
Copy link
Contributor

@ARYPROGRAMMER ARYPROGRAMMER commented Oct 8, 2025

Type of Change

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

Description

This PR implements the amount conversion framework for the Powertranz connector, transitioning from deprecated f64-based amount handling to the standardized type-safe FloatMajorUnit system.

Changes Made:

  • Added AmountConvertor trait implementation with FloatMajorUnitForConnector to the Powertranz struct
  • Implemented new() constructor for static initialization with the amount converter
  • Created PowertranzRouterData<T> wrapper struct to hold converted amounts alongside router data
  • Converted all amount fields from f64 to FloatMajorUnit in PowertranzPaymentsRequest and PowertranzBaseRequest
  • Updated TryFrom implementations for Authorize, Capture, and Refund flows to use the wrapper pattern
  • Replaced deprecated to_currency_base_unit_asf64() calls with convert_amount() utility function
  • Updated connector initialization in connector_mapping.rs and feature_matrix.rs to use Powertranz::new()
  • Updated test file to use the new constructor pattern

Files Modified:

  1. crates/hyperswitch_connectors/src/connectors/powertranz.rs
  2. crates/hyperswitch_connectors/src/connectors/powertranz/transformers.rs
  3. crates/router/src/types/api/connector_mapping.rs
  4. crates/router/src/types/api/feature_matrix.rs
  5. crates/router/tests/connectors/powertranz.rs

Additional Changes

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

Motivation and Context

Fixes #9086

This change is required to standardize amount handling across all connectors in the Hyperswitch platform. The old f64-based approach has been deprecated in favor of a type-safe amount conversion framework that properly handles different currency decimal formats (zero-decimal currencies like JPY, two-decimal currencies like USD, and three-decimal currencies like BHD).

The implementation follows the established pattern from PR #8878 (BankofAmerica connector) to ensure consistency across the codebase and proper handling of currency conversions.

How did you test it?

  • Verified all code compiles without errors using cargo check
  • Ran cargo +nightly fmt --all to ensure proper formatting
  • Verified no compilation errors in all 5 modified files
  • Confirmed removal of all deprecated to_currency_base_unit_asf64() method calls
  • Validated that the implementation follows the exact pattern from the BankofAmerica connector (PR refactor(connector): implement amount converter framework for authorizedotnet, bankofamerica #8878)
  • Existing unit tests updated to use the new constructor pattern
Payments Create

Request


{
    "amount": 9900,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "customer_id": "customer_123",
    "email": "guest@example.com",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "setup_future_usage": "off_session",
    "payment_method": "card",
    "all_keys_required": true,
    "customer_acceptance": {
        "acceptance_type": "online",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "01",
            "card_exp_year": "2027",
            "card_holder_name": "joseph Doe",
            "card_cvc": "100"
        }
    },
    "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"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "128.0.0.1"
    }
}

Response:


{
    "payment_id": "pay_L7EMvA4sIIIRNhDmeeSf",
    "merchant_id": "merchant_1760515718",
    "status": "succeeded",
    "amount": 9900,
    "net_amount": 9900,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 9900,
    "connector": "powertranz",
    "client_secret": "pay_L7EMvA4sIIIRNhDmeeSf_secret_FCRJClExPuf2zIymkKZU",
    "created": "2025-10-15T08:09:15.104Z",
    "currency": "USD",
    "customer_id": "customer_123",
    ....
}

Capture

curl --location 'http://localhost:8080/payments/pay_lXiPRi0HZuCpPfKVgjrG/capture' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: *****' \
--data '{
    "amount_to_capture": 9900
}'

Response:


{
    "payment_id": "pay_lXiPRi0HZuCpPfKVgjrG",
    "merchant_id": "merchant_1760515718",
    "status": "succeeded",
    "amount": 9900,
    "net_amount": 9900,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 9900,
    "connector": "powertranz",
    "client_secret": "pay_lXiPRi0HZuCpPfKVgjrG_secret_sJLDRrekz78vnxXRodoP",
    "created": "2025-10-15T08:14:37.339Z",
    "currency": "USD",
    "customer_id": "customer_123",
    "customer": {
        "id": "customer_123",
        "name": null,
        "email": "guest@example.com",
        "phone": null,
        "phone_country_code": null
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "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": null,
            "card_exp_month": "01",
            "card_exp_year": "2027",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null,
            "origin_zip": null
        },
        "phone": null,
        "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",
            "origin_zip": null
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": null,
    "phone": null,
    "return_url": "https://duck.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": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": null,
    "connector_transaction_id": "3577487b-3d59-444b-bd9f-c3c662dbd362",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": {
        "redirect_response": null,
        "search_tags": null,
        "apple_pay_recurring_details": null,
        "gateway_system": "direct"
    },
    "reference_id": "pay_lXiPRi0HZuCpPfKVgjrG_1",
    "payment_link": null,
    "profile_id": "pro_qsdkyrcTxLtN8foDby8g",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_fU2vqdnDs2HOh3f6TiMY",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-10-15T08:29:37.339Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "128.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_channel": null,
    "payment_method_id": "pm_NReCXyea1sDuerbXjSAg",
    "network_transaction_id": null,
    "payment_method_status": null,
    "updated": "2025-10-15T08:15:03.986Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "request_extended_authorization": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null,
    "enable_partial_authorization": null,
    "enable_overcapture": null,
    "is_overcapture_enabled": null,
    "network_details": null,
    "is_stored_credential": null,
    "mit_category": null
}

Void

{
    "payment_id": "pay_j8FxlMBgvNJhsxMKcqvp",
    "merchant_id": "merchant_1760515718",
    "status": "cancelled",
    "amount": 9900,
    "net_amount": 9900,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "powertranz",
    "client_secret": "pay_j8FxlMBgvNJhsxMKcqvp_secret_GD723c4FWuBX0N2nZjZr",
    "created": "2025-10-15T08:16:45.504Z",
    "currency": "USD",
    "customer_id": "customer_123",
    "customer": {
        "id": "customer_123",
        "name": null,
        "email": "guest@example.com",
        "phone": null,
        "phone_country_code": null
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "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": null,
            "card_exp_month": "01",
            "card_exp_year": "2027",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null,
            "origin_zip": null
        },
        "phone": null,
        "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",
            "origin_zip": null
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": null,
    "phone": null,
    "return_url": "https://duck.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": "mistake",
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": null,
    "connector_transaction_id": "baf77dfa-1f73-423e-90e4-c5b1697cdc8f",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": {
        "redirect_response": null,
        "search_tags": null,
        "apple_pay_recurring_details": null,
        "gateway_system": "direct"
    },
    "reference_id": "pay_j8FxlMBgvNJhsxMKcqvp_1",
    "payment_link": null,
    "profile_id": "pro_qsdkyrcTxLtN8foDby8g",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_fU2vqdnDs2HOh3f6TiMY",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-10-15T08:31:45.504Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "128.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_channel": null,
    "payment_method_id": "pm_8PL1PojlXBwLoOWrEteS",
    "network_transaction_id": null,
    "payment_method_status": null,
    "updated": "2025-10-15T08:16:56.122Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "request_extended_authorization": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null,
    "enable_partial_authorization": null,
    "enable_overcapture": null,
    "is_overcapture_enabled": null,
    "network_details": null,
    "is_stored_credential": null,
    "mit_category": null
}

Refund

Request:


curl --location 'http://localhost:8080/refunds' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: ************ \
--data '{
    "payment_id": "pay_LBEWKkKW8emAlfyO2y13",
    "amount": 9900,
    "reason": "Customer returned product",
    "refund_type": "instant"
}'

Response:


{
    "refund_id": "ref_KD9Zn7O8R3P0bEJSmgAq",
    "payment_id": "pay_LBEWKkKW8emAlfyO2y13",
    "amount": 9900,
    "currency": "USD",
    "status": "succeeded",
    "reason": "Customer returned product",
    "metadata": null,
    "error_message": null,
    "error_code": null,
    "unified_code": null,
    "unified_message": null,
    "created_at": "2025-10-15T08:18:16.896Z",
    "updated_at": "2025-10-15T08:18:18.057Z",
    "connector": "powertranz",
    "profile_id": "pro_qsdkyrcTxLtN8foDby8g",
    "merchant_connector_id": "mca_fU2vqdnDs2HOh3f6TiMY",
    "split_refunds": null,
    "issuer_error_code": null,
    "issuer_error_message": 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

Signed-off-by: Arya Pratap Singh <notaryasingh@gmail.com>
@ARYPROGRAMMER ARYPROGRAMMER requested review from a team as code owners October 8, 2025 11:48
@semanticdiff-com
Copy link

semanticdiff-com bot commented Oct 8, 2025

@ARYPROGRAMMER
Copy link
Contributor Author

can someone review this please. thanks

cc: @pixincreate

@ARYPROGRAMMER
Copy link
Contributor Author

Reminder for this @pixincreate . thanks

@ARYPROGRAMMER
Copy link
Contributor Author

@jagan-jaya and @mrudulvajpayee4935 , please review

Copy link
Contributor

@Vani-1107 Vani-1107 left a comment

Choose a reason for hiding this comment

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

Converted all amount fields from f64 to StringMajorUnit in PowertranzPaymentsRequest and PowertranzBaseRequest

Hi @ARYPROGRAMMER ,
We’ve converted f64 to StringMajorUnit here, but it should instead be converted to FloatMajorUnit. Please refer to the implementation here, if needed.

@ARYPROGRAMMER
Copy link
Contributor Author

Converted all amount fields from f64 to StringMajorUnit in PowertranzPaymentsRequest and PowertranzBaseRequest

Hi @ARYPROGRAMMER , We’ve converted f64 to StringMajorUnit here, but it should instead be converted to FloatMajorUnit. Please refer to the implementation here, if needed.

Updated to use FloatMajorUnit as suggested - Powertranz originally used f64, so I've now correctly implemented FloatMajorUnitForConnector following the pattern from Authorizedotnet and other similar connectors.

Thanks for the response. This is pending since too long

Copy link
Contributor

@Vani-1107 Vani-1107 left a comment

Choose a reason for hiding this comment

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

@ARYPROGRAMMER , please run cargo check command and address the errors and warnings you get.

@ARYPROGRAMMER
Copy link
Contributor Author

@ARYPROGRAMMER , please run cargo check command and address the errors and warnings you get.

hey @Vani-1107 ,

everything LGTM, here's the cargo check o/p:

image

Cached checks are not shown

took me 1 hour to debug and check fmt of this small thing. Glad to contribute

Vani-1107
Vani-1107 previously approved these changes Oct 13, 2025
Copy link
Contributor

@Vani-1107 Vani-1107 left a comment

Choose a reason for hiding this comment

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

Sure, thanks for contributing @ARYPROGRAMMER.
The changes looks good to me !

@ARYPROGRAMMER
Copy link
Contributor Author

Sure, thanks for contributing @ARYPROGRAMMER. The changes looks good to me !

Can you merge it. @Vani-1107

@deepanshu-iiitu deepanshu-iiitu added A-connector-integration Area: Connector integration C-bug Category: Bug hacktoberfest Issues that are up for grabs for Hacktoberfest participants labels Oct 14, 2025
jagan-jaya
jagan-jaya previously approved these changes Oct 15, 2025
@Vani-1107
Copy link
Contributor

Updated to use FloatMajorUnit as suggested - Powertranz originally used f64, so I've now correctly implemented FloatMajorUnitForConnector

@ARYPROGRAMMER , please change the PR description accordingly

@ARYPROGRAMMER
Copy link
Contributor Author

Updated to use FloatMajorUnit as suggested - Powertranz originally used f64, so I've now correctly implemented FloatMajorUnitForConnector

@ARYPROGRAMMER , please change the PR description accordingly

Donee

@Vani-1107
Copy link
Contributor

Vani-1107 commented Oct 15, 2025

Please address the required checks failure.
You can run just clippy and just clippy_v2 and address the errors and warnings you get.
Once you are done with the above changes, format the code using cargo +nightly fmt --all

Signed-off-by: Arya Pratap Singh <notaryasingh@gmail.com>
@ARYPROGRAMMER
Copy link
Contributor Author

ARYPROGRAMMER commented Oct 15, 2025

Please address the required checks failure. You can run just clippy and just clippy_v2 and address the errors and warnings you get. Once you are done with the above changes, format the code using cargo +nightly fmt --all

i tried to run all, please check

please help me if any further issues thanks

edit 2: also the reviews got dismissed as i pushed a new commit @Vani-1107

edit 3:

I increased my wsl memory to max , now i was able to run just clippy and just clippy_v2 successfully and cargo +nightly fmt -all didnt have any new changes.

here's the ss:

image image

the ci-pr/spell check error seems to originate from previous commits and not my changes

thanks @Vani-1107

@ARYPROGRAMMER
Copy link
Contributor Author

this ci-pr/spell check which is failing is originating from other files which this pr does not touch, rest should work. i triple checked

@ARYPROGRAMMER
Copy link
Contributor Author

ARYPROGRAMMER commented Oct 15, 2025

lets actually get this done now, i have updated the branch and ran all commands in local , above ss attached.

thanks to all reviewers.

let me know further @Vani-1107 , sorry to disturb a lot by pinging. if still there are any issues please help me fix them. thanks

@likhinbopanna likhinbopanna added this pull request to the merge queue Oct 16, 2025
Merged via the queue into juspay:main with commit 6c4084e Oct 16, 2025
19 of 21 checks passed
@pixincreate pixincreate added the hacktoberfest-accepted Pull requests accepted as Hacktoberfest contributions label Oct 16, 2025
drdholu pushed a commit to drdholu/hyperswitch that referenced this pull request Oct 30, 2025
…y#9752)

Signed-off-by: Arya Pratap Singh <notaryasingh@gmail.com>
aadityaguptaa pushed a commit that referenced this pull request Nov 10, 2025
Signed-off-by: Arya Pratap Singh <notaryasingh@gmail.com>
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-bug Category: Bug hacktoberfest Issues that are up for grabs for Hacktoberfest participants hacktoberfest-accepted Pull requests accepted as Hacktoberfest contributions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR]: [connector] Add amount conversion framework to Powertranz

6 participants