Skip to content

Conversation

@sakksham7
Copy link
Contributor

@sakksham7 sakksham7 commented Nov 13, 2025

Type of Change

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

Description

#1283

  • Added UPI Intent flow (App Redirection)
    From list of apps detect which ones are available based upon platform and device, and navigate to the soecific app for payment completion
    Request
{
  "payment_method": "upi",
  "payment_method_type": "upi_intent",
  "payment_method_data": {
    "upi": {
      "upi_intent": {}
    }
  },
}

Response

{
  "next_action": {
    "type": "sdk_upi_intent_information",
    "sdk_uri": "upi://pay?pa=upi@razopay&pn=MWSolutions&tr=sSp9yyvA8a1r7U3&tn=razorpay&am=46&cu=INR&mc=5411"
  },
  }
  • Added UPI Collect
    Collect the required fields using SDK and then show a waiting screen for the payment to be done
    Request and Response for UPI Collect Flow
{
  "payment_method": "upi",
  "payment_method_type": "upi_collect",
  "payment_method_data": {
    "upi": {
      "upi_collect": {
        "vpa_id": "success@razorpay"
      }
    },
    "billing": {
      "phone": {
        "number": "8056594427",
        "country_code": "+91"
      },
      "email": "[email protected]"
    }
  }
}

Response

{
"next_action": {
        "type": "wait_screen_information",
        "display_from_timestamp": 1762163470775955000,
        "display_to_timestamp": 1762163770775955000,
        "poll_config": {
            "delay_in_secs": 2,
            "frequency": 5
        }
    },
}
  • Added UPI QR Flow
    Generate the QR Code for the UPI Deep link which can be scanned using any UPI App
    Request
{
  "payment_method": "upi",
  "payment_method_type": "upi_qr",
  "payment_method_data": {
    "upi": {
      "upi_qr": {}
    }
  },
}

Response

{
  "next_action": {
    "type": "sdk_upi_intent_information",
    "sdk_uri": "upi://pay?pa=upi@razopay&pn=MWSolutions&tr=sSp9yyvA8a1r7U3&tn=razorpay&am=46&cu=INR&mc=5411"
  },
  }
  • Added three new screens - Waiting screen, QR Code display, App selection screen

  • Added dynamic polling and waiting timer logic based upon the response from confirm api
    Polling Object

"next_action": {
        "display_from_timestamp": 1762758753275718000,
        "display_to_timestamp": 1762759053275718000,
        "poll_config": {
        "delay_in_secs": 5,
        "frequency": 5
            }
        }
    }

Relevant Docs, Threads and Repos -
https://developer.mozilla.org/en-US/docs/Web/API/PaymentRequest/PaymentRequest
https://bitbucket.juspay.net/projects/PICAF/repos/qr-generator/browse
https://bitbucket.juspay.net/projects/PICAF/repos/hyper-sdk-web/browse
https://juspay.slack.com/archives/C03V08QDR24/p1759840819011839
https://juspay.slack.com/archives/C04TLRJEY4F/p1760334063176879

Enhancements need to be picked next :-
Maintain App List on S3
Improve Design
Add Locale Support
Move Qr Code library to shared codebase

How did you test it?

Tested UPI Collect via HS

Tested UPI QR and UPI Intent via UCS

https://github.com/user-attachments/assets/9716cf06-eb5c-4a31-a512-829dd6bf07de
https://github.com/user-attachments/assets/1968a982-b194-4a9c-9b10-9f742433c411

Checklist

  • I ran npm run re:build
  • I reviewed submitted code
  • I added unit tests for my changes where possible

@sakksham7 sakksham7 self-assigned this Nov 13, 2025
@sakksham7 sakksham7 added the Ready for Review PR with label Ready for Review should only be reviewed. label Nov 13, 2025
@semanticdiff-com
Copy link

semanticdiff-com bot commented Nov 13, 2025

Review changes with  SemanticDiff

Changed Files
File Status
  public/icons/orca.svg  5% smaller
  src/App.res Unsupported file format
  src/Components/ProgressBar.res Unsupported file format
  src/Components/UPIAvailableAppsModal.res Unsupported file format
  src/Components/UPIButtons.res Unsupported file format
  src/Components/UPIQRCode.res Unsupported file format
  src/Components/UPIWaitModal.res Unsupported file format
  src/PaymentElement.res Unsupported file format
  src/PaymentElementV2.res Unsupported file format
  src/Payments/PaymentMethodsRecord.res Unsupported file format
  src/Payments/UPI.res Unsupported file format
  src/Payments/UPIDisplay.res Unsupported file format
  src/Types/PaymentConfirmTypes.res Unsupported file format
  src/Types/PaymentModeType.res Unsupported file format
  src/Types/UPITypes.res Unsupported file format
  src/Utilities/PaymentHelpers.res Unsupported file format
  src/Utilities/PaymentUtils.res Unsupported file format
  src/Utilities/UPIHelpers.res Unsupported file format
  src/Utilities/Utils.res Unsupported file format
  src/WalletElement.res Unsupported file format
  src/hyper-loader/Elements.res Unsupported file format
  src/hyper-loader/Hyper.res Unsupported file format
  src/libraries/qr-code-generator/QRGenerator.res Unsupported file format
  src/libraries/qr-code-generator/qr-generator.js  0% smaller
  webpack.common.js  0% smaller

Comment on lines 52 to 63
// UPI App URLs for PaymentRequest API
"https://tez.google.com/pay",
"https://mercury.phonepe.com/transact/pay",
"https://securegw.paytm.in/order/sendpaymentrequest",
"https://payments.juspay.in/bhim/pay",
"https://cred-web-stg.dreamplug.in/checkout/pay",
"https://cred.club/checkout/pay",
"https://gokiwi.in/pay",
"https://pl.navifinserv.com/payments-gateway",
"https://super.money/pay",
"https://promotions.mobikwik.com/epay/payments-gateway/",
"https://moneyview.in/payment/",
Copy link
Contributor

Choose a reason for hiding this comment

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

I found that some UPI APIs are common in both script and connect sources. Please maintain a separate array for them, similar to localhostSources in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@val external backendEndPoint: string = "backendEndPoint"
@val external confirmEndPoint: string = "confirmEndPoint"
@val external sdkUrl: string = "sdkUrl"
// let sdkUrl = "https://9d18ffafef48f6.lhr.life"
Copy link
Contributor

Choose a reason for hiding this comment

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

remove it

Comment on lines +20 to +29

let isItInappBrowser = () => {
try {
let ua = navigator.userAgent->String.toLowerCase
ua->String.includes("instagram") || ua->String.includes("fbav")
} catch {
| _error => false
}
}

Copy link
Contributor

@aritro2002 aritro2002 Nov 13, 2025

Choose a reason for hiding this comment

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

Can you explain why you’re wrapping isItInappBrowser in a try-catch block? You didn’t do the same in getMobileOperatingSystem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When detecting isItInappBrowser, It might get called in restricted environments, whereas it is not the case with getMobileOperatingSystem

Comment on lines 72 to 83
dict
->Dict.get("app")
->Option.getOr(JSON.Encode.null)
->JSON.Decode.string
->Option.getOr("")

let packageName =
dict
->Dict.get("packageName")
->Option.getOr(JSON.Encode.null)
->JSON.Decode.string
->Option.getOr("")
Copy link
Contributor

Choose a reason for hiding this comment

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

use getStringFromDict

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added Utility function getStringFromDict, changed previous one to getStringFromOptionalDict

Comment on lines 149 to 153
let headersDict =
metaDataDict
->getJsonObjectFromDict("headers")
->JSON.Decode.object
->Option.getOr(Dict.make())
Copy link
Contributor

Choose a reason for hiding this comment

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

use getDictFromDict

Comment on lines 240 to 245
<RenderIf condition={paymentMethodType === "upi_qr"}>
<UPIQRCode upiUrl timeRemaining />
</RenderIf>
<RenderIf condition={paymentMethodType !== "upi_qr"}>
<UPIAvailableAppsModal availableApps selectedApp setSelectedApp />
</RenderIf>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do like this? use ternary operator

{
paymentMethodType === "upi_qr"?
<UPIQRCode upiUrl timeRemaining /> :
<UPIAvailableAppsModal availableApps selectedApp setSelectedApp />
}

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 saw that in our code we mostly have <RenderIf> based approach but at places we use ternary operator as well, so which one should be prioritized? @ArushKapoorJuspay @AbhishekChorotiya Can you also comment about this

Comment on lines +248 to +255
<RenderIf condition={paymentMethodType === "upi_qr"}>
<UPIButtons.DoneButton closeModal />
</RenderIf>
<RenderIf condition={paymentMethodType !== "upi_qr"}>
<UPIButtons.AppSelectionButton
selectedApp upiUrl setCurrentScreen setTimeRemaining timer defaultTimer
/>
</RenderIf>
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

Comment on lines 958 to 968
dict
->Dict.get("url")
->Option.getOr(JSON.Encode.null)
->JSON.Decode.string
->Option.getOr("")
let appName =
dict
->Dict.get("app")
->Option.getOr(JSON.Encode.null)
->JSON.Decode.string
->Option.getOr("")
Copy link
Contributor

Choose a reason for hiding this comment

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

use getStringFromDict

Comment on lines 11 to 12
background: themeObj.colorPrimary,
color: "#ffffff",
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use colors from themeObj?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment on lines 94 to 99
<button
className="w-full p-3 rounded-lg font-medium"
style={
background: themeObj.colorPrimary,
color: "#ffffff",
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

Comment on lines +51 to +62
<RenderIf condition={availableApps->Array.length !== 0}>
{availableApps
->Array.mapWithIndex((obj, i) =>
renderAppButton(obj, i, ~appArrayLen=availableApps->Array.length)
)
->React.array}
</RenderIf>
<RenderIf condition={availableApps->Array.length === 0}>
<div className="text-center text-gray-500 py-6">
{React.string("No UPI apps are available at the moment.")}
</div>
</RenderIf>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please use ternary operator here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for Review PR with label Ready for Review should only be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants