Braintree Hosted Fields在Polymer及iframe中部署失败求助
解决Polymer中Braintree Hosted Fields的HOSTED_FIELDS_TIMEOUT问题
我明白你在Polymer环境里整合Braintree Hosted Fields时遇到的头疼问题——官方暂不支持直接集成,改用iframe嵌入后端支付页又触发超时错误,甚至适配官方示例也卡在braintree.hostedFields.create这一步。咱们一步步拆解问题,找到可行的解决思路:
问题根源分析
- Shadow DOM的隔离限制:你手动创建了Shadow Root来挂载支付字段,但Braintree Hosted Fields需要直接访问DOM节点来注入iframe字段,Shadow DOM的封闭性会导致Braintree无法正确定位到
#card-number这类选择器,最终触发超时。 - iframe上下文/跨域限制:当把支付页嵌入iframe时,Braintree脚本可能因为跨域策略或iframe的安全限制,无法完成字段初始化流程,进而触发超时错误。
可行解决方案
方案1:绕过Shadow DOM,直接在Polymer组件主DOM渲染字段
Polymer组件默认的主DOM没有Shadow DOM的隔离问题,你可以调整组件结构,把Braintree字段模板放在主DOM中,而非手动创建Shadow Root:
class BraintreePayment extends Polymer.Element { static get template() { return html` <div id="braintree-fields"> <div id="card-number"></div> <div id="cvv"></div> <div id="expiration-date"></div> <button id="submit">完成支付</button> </div> `; } connectedCallback() { super.connectedCallback(); this._initBraintree(); } _initBraintree() { braintree.client.create({ authorization: 'sandbox_g42y39zw_348pk9cgf3bgyw2b' }, (clientErr, clientInstance) => { if (clientErr) { console.error('客户端初始化失败:', clientErr); return; } const options = { client: clientInstance, styles: { 'input': { 'font-size': '14px' }, 'input.invalid': { 'color': 'red' }, 'input.valid': { 'color': 'green' } }, fields: { number: { selector: '#card-number', placeholder: '4111 1111 1111 1111' }, cvv: { selector: '#cvv', placeholder: '123' }, expirationDate: { selector: '#expiration-date', placeholder: '10/2019' } } }; braintree.hostedFields.create(options, (hostedFieldsErr, hostedFieldsInstance) => { if (hostedFieldsErr) { console.error('Hosted Fields初始化失败:', hostedFieldsErr); return; } this.$.submit.addEventListener('click', (event) => { event.preventDefault(); hostedFieldsInstance.tokenize((tokenizeError, payload) => { if (tokenizeError) { console.error('令牌化失败:', tokenizeError); } else { alert('请将以下nonce发送到后端: ' + payload.nonce); } }); }); }); }); } } customElements.define('braintree-payment', BraintreePayment);
方案2:优化iframe的安全与跨域配置
如果必须用iframe嵌入后端支付页,需要调整以下配置:
- 后端支付页的
Content-Security-Policy需允许Braintree相关域名(*.braintreegateway.com、*.paypal.com) - 为iframe配置宽松的
sandbox属性,至少允许脚本执行和同源访问:
<iframe src="/your-node-payment-page" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe>
- 确保iframe内部完全加载Braintree脚本,不依赖父页面的任何资源。
方案3:改用Braintree Drop-in UI替代Hosted Fields
Drop-in UI封装度更高,对DOM环境的兼容性更好,适合在Polymer这类框架中快速集成:
class BraintreeDropin extends Polymer.Element { static get template() { return html` <div id="braintree-dropin"></div> <button id="submit">完成支付</button> `; } connectedCallback() { super.connectedCallback(); this._initDropin(); } _initDropin() { braintree.dropin.create({ authorization: 'sandbox_g42y39zw_348pk9cgf3bgyw2b', container: '#braintree-dropin' }, (err, dropinInstance) => { if (err) { console.error('Drop-in初始化失败:', err); return; } this.$.submit.addEventListener('click', () => { dropinInstance.requestPaymentMethod((err, payload) => { if (err) { console.error('获取支付方式失败:', err); } else { console.log('支付nonce:', payload.nonce); // 此处发送nonce到后端完成支付 } }); }); }); } } customElements.define('braintree-dropin', BraintreeDropin);
内容的提问来源于stack exchange,提问作者Max0999




