“你的应用必须防御性地处理响应。”这句话出自一篇关于 Android UPI 支付的技术分享,它点出了一个很多开发者容易忽略的现实:我们无法控制外部 UPI 应用返回的数据格式,只能主动适配。
在印度,UPI 已经成为 Android 应用默认的支付方式。大多数 App 集成 UPI 时都采用基于 Intent 的标准流程——支付在外部 UPI 应用内完成,结果再传回调用方。整个过程中,开发者不用接触密码、PIN 码或任何支付凭证,却必须保证对每一步的掌控力。下面这张“支付流程全景图”就是想把这件事讲清楚:
![]()
核心流程四步走
App 构建 UPI URI → 通过 Intent 启动外部 UPI 应用 → 用户完成支付后返回 → App 拿到响应串并安全解析,分别对应成功、失败、取消三种状态。
看起来简单,但细节里处处是防御的功夫。
第一关:URI 构建必须用 Uri.Builder
UPI 支付请求使用 upi://pay 的基础 scheme,后面缀上收款方 VPA、商户名、金额(字符串形式)、币种(INR)、交易备注和交易参考号等参数。例子:upi://pay?pa=test@upi&pn=Test%20Merchant&am=100.00&cu=INR&tr=TXN123。
重点不是参数本身,而是编码方式。不要手动拼字符串,直接用 Kotlin 的 Uri.Builder,通过 appendQueryParameter 添加每个参数。这样能自动处理特殊字符的编码,避免因为商户名里有空格、符号而导致 URI 被第三方应用误读。
第二关:启动支付要弃用 onActivityResult
Activity Result API 是官方推荐的现代方案,生命周期感知、线程安全,也避免了 fragment 间回调的混乱。用 registerForActivityResult 拿到一个 upiLauncher,在回调里根据 resultCode 判断:RESULT_OK 就取出 response extra 交给解析函数;其他情况视为用户取消或异常,直接走取消逻辑。
第三关:响应解析要假设一切都是会变的
返回的响应格式通常像这样:Status=SUCCESS&txnId=123456&ApprovalRefNo=987654。但问题来了:不同 UPI 应用的字段名可能有细微差异,参数的顺序不固定,甚至某些字段在某些场景下会直接消失。
拿这段响应去写死键名拆分会出事儿。合理的做法是先把它拆成键值对,存入 Map,再用 getOrNull 或 containsKey 做存在性检查。即便已经拿到 Status=SUCCESS,也别默认它就代表交易已完成——部分应用可能在 Status 里用别的值表示“处理中”或“已提交”。所以,每一步判断都要加上“未知状态走兜底”的分支。
整条链路里,没有哪一步是“只要照着写就没问题”的。UPI 官方的规范给了基础框架,但各银行和第三方应用的实现才是真实的战场。开发者能做的,就是别基于任何乐观假设写代码——URI 构建做编码防护,Intent 启动用现代 API,响应解析保留防御性退路。这样,当某个印度本地银行的 UPI 应用突然换了返回字段的命名,你的 App 也不至于直接崩溃或把钱算错。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.