Feat: headless ios#37
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a headless (UI-less) payment session API intended for iOS, wiring new native methods through the React Native module and exposing a PaymentSession interface to JS, plus an example app screen demonstrating usage.
Changes:
- Introduces headless response parsing (
ResponseHandler) and a JS-facinginitPaymentSessionthat returns aPaymentSessionobject with headless methods. - Adds iOS native bridge methods for saved payment methods + confirming with default/last-used methods.
- Updates the example app to include a “Headless Mode” tab and UI for exercising the new APIs.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/@juspay-tech/react-native-hyperswitch/src/utils/ResponseHandler.res | Adds parsing helpers for standardized native headless responses. |
| packages/@juspay-tech/react-native-hyperswitch/src/types/HyperTypes.res | Adds headlessResponse and paymentSession types for JS consumption. |
| packages/@juspay-tech/react-native-hyperswitch/src/specs/NativeHyperswitchSdkReactNative.ts | Extends the native spec with headless methods. |
| packages/@juspay-tech/react-native-hyperswitch/src/modules/NativeHyperswitchSdk.res | Adds ReScript externals/types for headless native methods and saved PM data shapes. |
| packages/@juspay-tech/react-native-hyperswitch/src/index.tsx | Exposes initPaymentSession and headless-related types from the package entrypoint. |
| packages/@juspay-tech/react-native-hyperswitch/src/hooks/useWidget.res | Tightens typing around presentPaymentSheet promise resolution. |
| packages/@juspay-tech/react-native-hyperswitch/src/core/Hyper.res | Implements initPaymentSession returning a JS PaymentSession wrapper. |
| packages/@juspay-tech/react-native-hyperswitch/ios/Modules/ReactNative/HyperswitchSdkReactNative.mm | Exports new iOS headless methods to React Native. |
| packages/@juspay-tech/react-native-hyperswitch/ios/Modules/ReactNative/HyperswitchModule.swift | Implements iOS headless method behavior and response payloads. |
| example/src/styles.ts | Adds styles for headless UI. |
| example/src/UIScreen.tsx | Renames/updates UI-mode example screen. |
| example/src/HeadlessScreen.tsx | Adds a new headless-mode example screen using PaymentSession. |
| example/src/App.tsx | Adds a simple tab switcher between UI and headless examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.paymentSession?.getCustomerSavedPaymentMethods(initSavedPaymentMethodSessionCallback) | ||
|
|
||
| resolve(["status": "success", "message": "Payment methods initialized"]) |
There was a problem hiding this comment.
getCustomerSavedPaymentMethods resolves the promise immediately after calling paymentSession?.getCustomerSavedPaymentMethods(...), but PaymentSessionHandler is provided asynchronously via PaymentSession.headlessCompletion (see PaymentSession+UIKit.swift). This can race with JS calling getCustomerDefaultSavedPaymentMethodData/confirm... and hit the "handler not initialized" path even though initialization was reported as success. Consider resolving only after the callback sets paymentSessionHandler (or return an error if paymentSession is nil).
| self.paymentSession?.getCustomerSavedPaymentMethods(initSavedPaymentMethodSessionCallback) | |
| resolve(["status": "success", "message": "Payment methods initialized"]) | |
| guard let paymentSession = self.paymentSession else { | |
| resolve([ | |
| "status": "error", | |
| "code": "UNKNOWN", | |
| "message": "Payment session not initialized." | |
| ]) | |
| return | |
| } | |
| paymentSession.getCustomerSavedPaymentMethods { [weak self] handler in | |
| self?.initSavedPaymentMethodSessionCallback(handler: handler) | |
| resolve(["status": "success", "message": "Payment methods initialized"]) | |
| } |
| headlessResponseStatus as HeadlessResponseStatus, | ||
| cardDetails as CardDetails, | ||
| savedPaymentMethod as SavedPaymentMethod, | ||
| } from './modules/NativeHyperswitchSdk.gen'; |
There was a problem hiding this comment.
HeadlessResponse / HeadlessResponseStatus are exported from NativeHyperswitchSdk.gen, but PaymentSession methods actually return HyperTypes.headlessResponse (status strings like "success"/"failed"/"cancelled"/"error" with optional code/data). The exported status union ("succeeded"/"requires_action"/...) doesn’t match the iOS responses implemented in HyperswitchModule.swift, and conflicts with the example usage checking for status === "success". Export the headless response type from HyperTypes.gen (or align the native/status model so both definitions match).
| } from './modules/NativeHyperswitchSdk.gen'; | |
| } from './types/HyperTypes.gen'; |
| let status = | ||
| dict | ||
| ->Js.Dict.get("status") | ||
| ->Belt.Option.flatMap(Js.Json.decodeString) |
There was a problem hiding this comment.
use core rescript methods, remove Belt and Js methods
| public func getCustomerSavedPaymentMethods( | ||
| withResolve resolve: @escaping RCTPromiseResolveBlock, | ||
| reject: @escaping RCTPromiseRejectBlock | ||
| ) -> Void { |
There was a problem hiding this comment.
remove -> Void
| if let jsonData = try? JSONEncoder().encode(paymentMethod), | ||
| let jsonDict = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] { | ||
| resolve([ | ||
| "status": "success", | ||
| "message": "Default payment method retrieved", | ||
| "data": jsonDict | ||
| ]) | ||
| } else { | ||
| resolve([ | ||
| "status": "error", | ||
| "code": "ENCODE_ERROR", | ||
| "message": "Failed to encode payment method data" | ||
| ]) | ||
| } | ||
| case .failure(let error): | ||
| resolve([ | ||
| "status": "failed", | ||
| "code": error.code, | ||
| "message": error.message | ||
| ]) |
There was a problem hiding this comment.
we can try to make it passthrough
…witch into feat-headless-ios
ADDED
Usage GUIDE
1. Initialize the SDK
2. Wrap Your App with HyperElements
Headless Payments
For custom UIs, use the PaymentSession API for headless (UI-less) payment processing.