You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动