JusPay EC Headless:Android Kotlin 如何获取支付方式
结论
HyperServiceHolder 初始化成功后,不会自动返回 Payment Methods、Saved Cards 或 Available UPI。应用还需要完成以下步骤:
- 由服务端创建有效的 Juspay 订单或支付会话;
- 获取接口返回的 Headless SDK
process/fetchpayload; - 使用已经完成初始化的同一个
HyperServiceHolder实例调用process(activity, payload); - 在初始化时注册的
hyperServicecallback 中处理异步结果。
不要将文档里的示例 payload 直接用于真实请求。Saved Cards、UPI 等数据依赖实际的商户、客户、订单、认证令牌和环境信息,示例值通常无法产生有效结果。
正确的调用流程
调用顺序如下:
服务端创建订单或支付会话
↓
Android 初始化 HyperServiceHolder
↓
收到 initiate_result 成功事件
↓
Android 获取服务端返回的 fetch/process payload
↓
调用同一个 HyperServiceHolder.process(...)
↓
在同一个 callback 中处理 process_result
需要特别注意,process() 的结果不会通过方法返回值提供,而是异步发送到初始化阶段注册的 callback。
Kotlin 调用示例
下面的代码展示了完整的调用结构。不同 EC Headless SDK 版本的 callback 类型、字段名称和 process 方法签名可能略有不同,请以项目当前依赖版本提供的接口为准。
private lateinit var hyperServiceHolder: HyperServiceHolder
private val hyperPaymentsCallback =
object : HyperPaymentsCallbackAdapter() {
override fun onEvent(
event: JSONObject,
responseHandler: HyperPaymentsResponseHandler
) {
Log.d("JuspayEC", "SDK event: $event")
val eventName = event.optString("event")
val payload = event.optJSONObject("payload")
when (eventName) {
"initiate_result" -> {
val status = payload?.optString("status")
if (status.equals("SUCCESS", ignoreCase = true)) {
// 初始化已完成。
// 此时可以获取服务端生成的 fetch/process payload,
// 然后调用 fetchPaymentMethods()。
} else {
Log.e("JuspayEC", "Initialization failed: $event")
}
}
"process_result" -> {
handleFetchResult(payload, event)
}
else -> {
// 不要只过滤某一个事件。调试阶段应记录 SDK 返回的全部事件。
Log.d("JuspayEC", "Unhandled SDK event: $eventName")
}
}
}
}
执行 fetch/process:
private fun fetchPaymentMethods(
activity: FragmentActivity,
processPayload: JSONObject
) {
if (!::hyperServiceHolder.isInitialized) {
Log.e("JuspayEC", "HyperServiceHolder is not initialized")
return
}
Log.d("JuspayEC", "Calling process: $processPayload")
try {
hyperServiceHolder.process(activity, processPayload)
} catch (error: Exception) {
Log.e("JuspayEC", "Unable to call Juspay process", error)
}
}
部分 SDK 封装使用的是以下签名:
hyperServiceHolder.process(processPayload)
是否需要传入 Activity,取决于当前使用的 HyperServiceHolder 实现。不要参照其他版本的示例自行修改方法签名。
process payload 应从哪里获取
完整 payload 应由服务端根据当前商户配置、客户和订单动态生成,再交给 Android。客户端应尽量原样转交,不要自行拼接认证字段。
典型的外层结构类似:
{
"requestId": "unique-request-id",
"service": "in.juspay.ec",
"payload": {
"action": "<当前版本规定的 fetch action>",
"orderId": "<real-order-id>",
"merchantId": "<merchant-id>",
"clientId": "<client-id>",
"customerId": "<customer-id>",
"clientAuthToken": "<token-returned-by-server>"
}
}
这只是结构示意,并不表示所有字段都适用于当前 SDK 版本。以下内容尤其不能根据示例猜测:
action的具体取值;service的具体取值;clientAuthToken、orderId等认证或会话字段;- payload 是否还需要
environment、customerMobile等参数。
如果 Juspay 服务端接口已经返回完整的 sdk_payload 或 processPayload,Android 应直接使用该对象:
val processPayload = JSONObject(serverResponse.getString("sdk_payload"))
hyperServiceHolder.process(activity, processPayload)
如果 sdk_payload 只是外层对象中的 payload 部分,则需要按照当前文档补充 requestId 和 service。使用前应先确认服务端响应结构,避免出现重复包装:
{
"requestId": "...",
"service": "...",
"payload": {
"requestId": "...",
"service": "...",
"payload": {}
}
}
这种重复嵌套通常无法得到预期结果。
如何读取支付方式数据
收到 process_result 后,先检查状态,再读取返回数据。具体字段路径取决于 SDK 版本和 fetch action,不能假定所有版本都会直接返回 paymentMethods。
private fun handleFetchResult(
payload: JSONObject?,
rawEvent: JSONObject
) {
if (payload == null) {
Log.e("JuspayEC", "process_result does not contain payload: $rawEvent")
return
}
val status = payload.optString("status")
if (!status.equals("SUCCESS", ignoreCase = true)) {
val errorCode = payload.optString("errorCode")
val errorMessage = payload.optString("errorMessage")
Log.e(
"JuspayEC",
"Fetch failed: code=$errorCode, message=$errorMessage, event=$rawEvent"
)
return
}
// 字段名称必须根据实际 process_result 解析。
val paymentMethods = payload.optJSONArray("paymentMethods")
val savedCards = payload.optJSONArray("savedCards")
val availableUpi = payload.optJSONArray("availableUpi")
Log.d("JuspayEC", "paymentMethods=$paymentMethods")
Log.d("JuspayEC", "savedCards=$savedCards")
Log.d("JuspayEC", "availableUpi=$availableUpi")
}
如果这些字段是 null,不一定说明 SDK 没有返回数据。它们可能位于更深层的 data、result 或其他对象中。调试时应先记录完整的 process_result,再根据实际 JSON 结构建立数据模型。
为什么 Saved Cards 或 UPI 可能为空
Saved Cards 通常与已识别的客户绑定。即使 fetch 调用成功,以下情况仍可能返回空列表:
- 没有传入稳定且有效的
customerId; - 创建会话时没有完成客户认证;
- 当前客户没有保存过银行卡;
- 创建订单和初始化 SDK 时使用了不同环境;
- 商户后台没有启用相关支付方式;
- 当前订单的币种、金额或商户配置不支持该支付方式。
Available UPI 也不是固定列表。返回内容可能受到设备环境、已安装的 UPI 应用、商户配置、订单参数和 SDK 能力检测结果的影响。
process 没有 callback 时重点检查
如果调用 process() 后完全没有收到事件,请依次检查:
- 是否在收到初始化成功事件后才调用;
- 是否始终复用同一个
HyperServiceHolder实例; - callback 是否在初始化时正确注册,并且仍然存活;
- 传入的是否为当前版本要求的完整外层 payload;
service是否与初始化时一致;requestId是否非空,并且每次请求都唯一;- 订单、客户和认证令牌是否来自同一环境;
- 调用时传入的
Activity是否处于有效生命周期; - 是否只监听了初始化事件,忽略了
process_result或错误事件; - 是否在解析 JSON 前就因字段缺失抛出了异常。
调试阶段不要只记录成功分支,应打印 callback 收到的完整 JSON:
override fun onEvent(
event: JSONObject,
responseHandler: HyperPaymentsResponseHandler
) {
Log.d("JuspayEC", event.toString(2))
}
日志中可能包含订单或客户的敏感信息。正式环境必须对这些信息脱敏,并且不要记录认证令牌和完整卡片信息。
如果确认上述流程无误,但仍然没有收到任何 callback,需要将当前 SDK 版本、初始化 payload、脱敏后的 process payload 和完整 SDK 日志提供给 Juspay 支持团队确认。EC Headless 的 fetch action 和响应字段可能因集成方案或版本而异,仅凭通用 sample payload 无法可靠判断。
备注:内容仅供参考。
