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): customer_details storage in payment_intent #5007

Merged
merged 42 commits into from
Jun 28, 2024

Conversation

prajjwalkumar17
Copy link
Contributor

@prajjwalkumar17 prajjwalkumar17 commented Jun 13, 2024

Type of Change

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

Description

This change will insure storage of customer_details in our payment_intent table.

if customer_id alone is passed -> Derive customer_details from customer_table and put it in guest_customer_details,
if customer_id is not present -> Put the details in guest_customer_details.
if both are present -> we are ignoring the email, phone etc and deriving them from customer_table and adding to the guest_customer_details field.

Additional Changes

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

The Db Migration:

ALTER TABLE payment_intent ADD COLUMN IF NOT EXISTS customer_details BYTEA DEFAULT NULL;

Motivation and Context

How did you test it?

Testing Scenarios

Screenshot 2024-06-27 at 6 07 09 PM

Payment create request with no cus_id and fields name, email, phone, phone country code

Response should be populated like this

"customer_id": null,
"customer": {
    "id": null,
    "name": "light",
    "email": "guest@example.com",
    "phone": "9xxxxxxxxx",
    "phone_country_code": "+91"
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "email": "guest@example.com",
    "name": "light",
    "phone": "9xxxxxxxxx",
    "phone_country_code": "+91",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'
Screenshot 2024-06-14 at 1 34 46 AM

Payment create request with some random cus_id

Response should be populated like this

"customer_id": "id_12343",
"customer": {
    "id": null,
    "name": null,
    "email": null,
    "phone": null,
    "phone_country_code": null
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
   "customer_id": "id_12343",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'

Payment create request with a created customer's cus_id

Response should be populated like this

"customer_id": "id_12343",
"customer": {
    "id": null,
    "name": null,
    "email": null,
    "phone": null,
    "phone_country_code": null
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
   "customer_id": "id_12343",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'

Payment create request with cus_id and all other data

Response should be populated like this

"customer_id": null,
"customer": {
    "id": null,
    "name": "light",
    "email": "guest@example.com",
    "phone": "9xxxxxxxxx",
    "phone_country_code": "+91"
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
   "customer_id": "id_12343",
     "name": "light",
     "email": "guest@example.com",
     "phone": "9xxxxxxxxx",
      "phone_country_code": "+91",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'

Payment create request with name and payment confirm with email and phone

Response should be populated like this after confirm request

"customer_id": null,
"customer": {
    "id": null,
    "name": "light",
    "email": "guest@example.com",
    "phone": "9xxxxxxxxx",
    "phone_country_code": null
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
     "name": "light",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'
curl --location 'http://127.0.0.1:8080/payments/pay_7m0meCohmJxQJ4RzzlTm/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxxxxxxxxx' \
--data-raw '{
    "email": "guest@example.com",
    "phone": "9xxxxxxxxx",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4000003920000003",
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_cvc": "123"
        }
    } 
}'

Payment create request with name and payment update with email and phone

Response should be populated like this after update request

"customer_id": null,
"customer": {
    "id": null,
    "name": "light",
    "email": "payme@example.com",
    "phone": "9xxxxxxxxx",
    "phone_country_code": null
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
     "name": "light",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'
curl --location 'http://127.0.0.1:8080/payments/pay_7m0meCohmJxQJ4RzzlTm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Sz1d4HlpPQ71WmZksJ7NghSNmy64WGRkAFy0vhOmTA5QrDUVYNRb43ZSFySsz5KX' \
--data-raw '{
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "10",
            "card_exp_year": "2035",
            "card_holder_name": "John Doe",
            "card_cvc": "123"
        }
    },
    "email": "payme@example.com",   
    "phone": "9xxxxxxxxx"
}'

Payment create request with id, name and payment update with email and phone

Response should be populated like this after update request

"customer_id": "id_12343",
"customer": {
    "id": "id_12343",
    "name": "light",
    "email": "payme@example.com",
    "phone": "9xxxxxxxxx",
    "phone_country_code": null
},
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "customer_id": "id_12343",
     "name": "light",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'
curl --location 'http://127.0.0.1:8080/payments/pay_7m0meCohmJxQJ4RzzlTm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Sz1d4HlpPQ71WmZksJ7NghSNmy64WGRkAFy0vhOmTA5QrDUVYNRb43ZSFySsz5KX' \
--data-raw '{
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "10",
            "card_exp_year": "2035",
            "card_holder_name": "John Doe",
            "card_cvc": "123"
        }
    },
    "email": "payme@example.com",   
    "phone": "9xxxxxxxxx"
}'

MCA of BOA, email in Payment create request and then List Payment Method with client secret

Response should be populated like this after update request

{
"redirect_url": "https://google.com/success",
"currency": "USD",
"payment_methods": [
{
"payment_method": "wallet",
"payment_method_types": [
{
"payment_method_type": "google_pay",
"payment_experience": [
{
"payment_experience_type": "redirect_to_url",
"eligible_connectors": [
"bankofamerica"
]
}
],
"card_networks": null,
"bank_names": null,
"bank_debits": null,
"bank_transfers": null,
"required_fields": {
"billing.address.zip": {
"required_field": "payment_method_data.billing.address.zip",
"display_name": "zip",
"field_type": "user_address_pincode",
"value": "94122"
},
"email": {
"required_field": "email",
"display_name": "email",
"field_type": "user_email_address",
"value": "payme@example.com"
},
........

curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:xxxxx' \
--data-raw '
{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "email": "payme@example.com",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "setup_future_usage": "on_session",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "127.0.0.1",
            "user_agent": "amet irure esse"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
'
curl --location 'http://127.0.0.1:8080/account/payment_methods?client_secret=pay_7m0meCohmJxQJ4RzzlTm_secret_ybvSTH07m5MGTGQDHA3R' \
--header 'Accept: application/json' \
--header 'api-key: xxxxxxxxx' \
--data ''

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

@prajjwalkumar17 prajjwalkumar17 added A-core Area: Core flows M-database-changes Metadata: This PR involves database schema changes labels Jun 13, 2024
@prajjwalkumar17 prajjwalkumar17 added this to the May 2024 Release milestone Jun 13, 2024
@prajjwalkumar17 prajjwalkumar17 self-assigned this Jun 13, 2024
@prajjwalkumar17 prajjwalkumar17 requested review from a team as code owners June 13, 2024 20:06
@prajjwalkumar17 prajjwalkumar17 linked an issue Jun 14, 2024 that may be closed by this pull request
2 tasks
Base automatically changed from storage_changes_for_v2 to main June 19, 2024 11:55
@prajjwalkumar17 prajjwalkumar17 requested a review from a team as a code owner June 19, 2024 11:55
Copy link
Member

@Narayanbhat166 Narayanbhat166 left a comment

Choose a reason for hiding this comment

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

Try if we can update the customer details in payment intent only if necessary rather then updating all the time. This is not critical for now and can be taken up in a later PR as well

@hyperswitch-bot hyperswitch-bot bot added the M-api-contract-changes Metadata: This PR involves API contract changes label Jun 27, 2024
Comment on lines +508 to +518
// For the case when we don't have Customer data directly stored in Payment intent
let customer_table_response: Option<CustomerDetailsResponse> =
customer.as_ref().map(ForeignInto::foreign_into);

// If we have customer data in Payment Intent, We are populating the Retrieve response from the
// same
let customer_details_response =
if let Some(customer_details_raw) = payment_intent.customer_details.clone() {
let customer_details_encrypted =
serde_json::from_value::<CustomerData>(customer_details_raw.into_inner().expose());
if let Ok(customer_details_encrypted_data) = customer_details_encrypted {
Copy link
Member

Choose a reason for hiding this comment

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

you can move this to a function

Narayanbhat166
Narayanbhat166 previously approved these changes Jun 27, 2024
Copy link
Contributor

@apoorvdixit88 apoorvdixit88 left a comment

Choose a reason for hiding this comment

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

Looks fine for Dashboard specific changes

@Gnanasundari24 Gnanasundari24 added this pull request to the merge queue Jun 28, 2024
Merged via the queue into main with commit bb9a971 Jun 28, 2024
11 checks passed
@Gnanasundari24 Gnanasundari24 deleted the feat/guest_customer_details branch June 28, 2024 09:51
pixincreate added a commit that referenced this pull request Jun 28, 2024
…ror-handling-in-cypress

* 'main' of github.com:juspay/hyperswitch:
  Docs: Api reference docs update for Payments - Create (#4955)
  feat(cypress): add iatapay connector (#5093)
  chore: fix ui-test configs (#5152)
  chore(cards): add configuration option to change the decryption scheme locker (#5140)
  refactor(hyperswitch_constraint_graph): Removal of lifetime from the Constraint Graph framework (#5132)
  feat(core): customer_details storage in payment_intent (#5007)
  fix(connector): [ADYEN] send `browser_info` for all the card and googlepay payments (#5153)
  fix(users): clear cookie and alter parsing for sso (#5147)
  refactor(connector): added amount framework to paypal, payouts and routing (#4865)
  chore(version): 2024.06.28.0
  chore(postman): update Postman collection files
  chore: use generic phone numbers instead (#5142)
@Narayanbhat166
Copy link
Member

we should also consider the guest customer details that is provided as a part of the customer object in payments request.

pub customer: Option<CustomerDetails>,

The customer_id field is mandatory here, this should be made optional and allowed to pass guest customer information without the customer id field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-core Area: Core flows M-api-contract-changes Metadata: This PR involves API contract changes M-database-changes Metadata: This PR involves database schema changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE] Inclusion of customer details in payment_intent column
5 participants