React Native WebView向网站结账页注入虚拟卡信息方案咨询
React Native WebView虚拟卡支付信息自动填充方案
关于结账表单字段标识的统一问题
- 全球线上商店的结账表单没有统一的ID/Class/Name命名标准,不同建站工具(Shopify、WooCommerce、Magento等)、商家自研的结账页字段命名差异极大
- 不需要逐页手动翻源码查每个字段的ID,通过多维度匹配规则可以覆盖绝大多数主流站点,仅需要对少量做了特殊定制、混淆的站点做单独适配
- 字段匹配优先级从高到低:
- 标准
autocomplete属性:这是W3C定义的浏览器自动填充标准,正规站点都会给卡相关字段加cc-number/cc-exp/cc-csc这类autocomplete值,匹配准确率最高 - 输入框的
name属性:大部分站点的字段name会带card/exp/cvc这类关键词,不区分大小写匹配即可 - 输入框的
placeholder/关联label文本:匹配中文/英文的“卡号”“有效期”“CVC”这类提示文本 - 最后才是针对特殊站点的自定义ID/Class匹配
- 标准
按钮点击触发自动填充的实现代码
你提供的参考模板里的injectedJavaScript是页面加载时自动执行的,如果要做按钮点击触发注入,需要通过WebView实例的injectJavaScript方法动态执行脚本,完整实现和你给出的代码风格完全兼容:
import React, { Component } from 'react'; import { View, Button, StyleSheet } from 'react-native'; import { WebView } from 'react-native-webview'; export default class PaymentWebView extends Component { state = { webViewUrl: '替换为你要加载的商店结账页地址' } // 实际项目中这部分数据从你的平台接口/全局状态中获取 cardInfo = { number: '4242424242424242', expMonth: '12', expYear: '28', cvc: '123', holderName: 'ZHANG SAN' } // 执行卡信息注入 fillCardInfo = () => { const injectScript = ` // 通用字段匹配方法 function matchField(acList, nameList, phList) { // 优先匹配标准autocomplete属性 let target = document.querySelector(\`input[autocomplete*="\${acList[0]}"]\`); if (target) return target; // 其次匹配name属性(不区分大小写) for (const kw of nameList) { target = document.querySelector(\`input[name*="\${kw}" i]\`); if (target) return target; } // 最后匹配placeholder提示文本(不区分大小写) for (const kw of phList) { target = document.querySelector(\`input[placeholder*="\${kw}" i]\`); if (target) return target; } return null; } // 填充卡号 const cardNumInput = matchField( ['cc-number'], ['cardnumber', 'cardnum', 'ccnum', 'card_no'], ['卡号', 'card number'] ); if (cardNumInput) { cardNumInput.value = '${this.cardInfo.number}'; // 触发原生事件,兼容React/Vue等框架写的表单校验 cardNumInput.dispatchEvent(new Event('input', {bubbles: true})); cardNumInput.dispatchEvent(new Event('change', {bubbles: true})); } // 填充有效期 const expInput = matchField( ['cc-exp'], ['exp', 'expiry', 'expdate', 'valid'], ['有效期', 'expiry', 'MM/YY', '到期日'] ); if (expInput) { expInput.value = '${this.cardInfo.expMonth}/${this.cardInfo.expYear}'; expInput.dispatchEvent(new Event('input', {bubbles: true})); expInput.dispatchEvent(new Event('change', {bubbles: true})); } // 填充CVC安全码 const cvcInput = matchField( ['cc-csc'], ['cvc', 'cvv', 'ccv', 'security'], ['安全码', 'cvc', 'cvv'] ); if (cvcInput) { cvcInput.value = '${this.cardInfo.cvc}'; cvcInput.dispatchEvent(new Event('input', {bubbles: true})); cvcInput.dispatchEvent(new Event('change', {bubbles: true})); } // 填充持卡人姓名 const nameInput = matchField( ['cc-name'], ['cardholder', 'holdername', 'cardname'], ['持卡人', 'card holder', '姓名'] ); if (nameInput) { nameInput.value = '${this.cardInfo.holderName}'; nameInput.dispatchEvent(new Event('input', {bubbles: true})); nameInput.dispatchEvent(new Event('change', {bubbles: true})); } true; `; this.webview.injectJavaScript(injectScript); } render() { return ( <View style={styles.container}> <WebView ref={ref => { this.webview = ref; }} source={{ uri: this.state.webViewUrl }} originWhitelist={['*']} javaScriptEnabledAndroid={true} domStorageEnabled={true} /> {/* 点击按钮触发自动填充 */} <Button title="自动填充虚拟卡信息" onPress={this.fillCardInfo} /> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1 } });
实现注意事项:
- 给输入框赋值后必须手动触发
input和change事件,现在绝大多数电商站的表单都是前端框架渲染的,直接修改value属性不会同步到框架的状态里,会被判定为未输入- 遇到有效期拆成“月份”“年份”两个独立输入框、或者卡表单嵌在跨域iframe里的特殊站点,单独给对应域名加适配规则即可,不需要修改通用匹配逻辑
- 你提供的测试结账页用上面的通用匹配规则就可以直接完成填充,不需要额外扒取字段ID

内容的提问来源于stack exchange,提问作者Abenezer Bayeh
相关产品推荐
相关产品推荐

