61

我正在构建一个简单的网站,将使用 Stripe 处理付款。我正在使用 Bootstrap 进行样式设置。当我使用 Stripe Elements 插入付款字段时,它们没有使用 Bootstrap 进行样式设置。如何将 Bootstrap 的样式应用于 Elements 支付字段?

4

6 回答 6

56

好吧,所以我必须弄清楚这一点,因为我使用的是 Stripe.js v2,并且 Stripe 技术支持向我解释了安全漏洞,所以我觉得有义务切换到 Stripe.js v3 “Elements”。他们所说的是,与您的信用卡表单元素在同一页面上的任何 javascript 都可以获取敏感信用卡数据的值。我想如果一个人正在拉入外部脚本,这可能会发生......而且我想它一定已经发生了,否则他们不会关心它。无论如何,这就是我如何让我的 Stripe.js v3 元素与 Bootstrap 4 输入组一起工作。这是一个完整的工作示例,您只需要更改公钥。

默认的基于 jQuery 的示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Stripe.js v3 with Bootstrap 4 Test</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <style>
        /* Blue outline on focus */
        .StripeElement--focus {
            border-color: #80BDFF;
            outline:0;
            box-shadow: 0 0 0 .2rem rgba(0,123,255,.25);
            transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
        }
        /* Can't see what I type without this */
        #card-number.form-control,
        #card-cvc.form-control,
        #card-exp.form-control {
            display:inline-block;
        }
    </style>
</head>
<body>

    <div class="container-fluid">
        <h1 class="mt-5">Stripe.js v3 with Bootstrap 4 (beta) Test</h1>
        <div id="card-errors" role="alert"></div>
        <div class="card">
            <div class="card-body">
                <form id="payment-form">
                    <label for="name">Name on Card</label>
                    <div class="input-group mb-2">
                        <div class="input-group-prepend">
                            <span class="input-group-text">A</span>
                        </div>
                        <input type="text" class="form-control" id="name">
                        <div class="input-group-append">
                            <span class="input-group-text">B</span>
                        </div>
                    </div>
                    <label for="card-number">Credit Card Number</label>
                    <div class="input-group mb-2">
                        <div class="input-group-prepend">
                            <span class="input-group-text">C</span>
                        </div>
                        <span id="card-number" class="form-control">
                            <!-- Stripe Card Element -->
                        </span>
                        <div class="input-group-append">
                            <span class="input-group-text">D</span>
                        </div>
                    </div>
                    <label for="card-cvc">CVC Number</label>
                    <div class="input-group mb-2">
                        <div class="input-group-prepend">
                            <span class="input-group-text">E</span>
                        </div>
                        <span id="card-cvc" class="form-control">
                            <!-- Stripe CVC Element -->
                        </span>
                    </div>
                    <label for="card-exp">Expiration</label>
                    <div class="input-group mb-2">
                        <span id="card-exp" class="form-control">
                            <!-- Stripe Card Expiry Element -->
                        </span>
                        <div class="input-group-append">
                            <span class="input-group-text">F</span>
                        </div>
                    </div>
                    <button id="payment-submit" class="btn btn-primary mt-1">Submit Payment</button>
                </form>
            </div>
        </div>
    </div>

<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<script src="https://js.stripe.com/v3/"></script>
<script>
    $(document).ready(function(){

        // Create a Stripe client
        var stripe = Stripe('pk_test_XxXxXxXxXxXxXxXxXxXxXxXx');

        // Create an instance of Elements
        var elements = stripe.elements();

        // Try to match bootstrap 4 styling
        var style = {
            base: {
                'lineHeight': '1.35',
                'fontSize': '1.11rem',
                'color': '#495057',
                'fontFamily': 'apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'
            }
        };

        // Card number
        var card = elements.create('cardNumber', {
            'placeholder': '',
            'style': style
        });
        card.mount('#card-number');

        // CVC
        var cvc = elements.create('cardCvc', {
            'placeholder': '',
            'style': style
        });
        cvc.mount('#card-cvc');

        // Card expiry
        var exp = elements.create('cardExpiry', {
            'placeholder': '',
            'style': style
        });
        exp.mount('#card-exp');

        // Submit
        $('#payment-submit').on('click', function(e){
            e.preventDefault();
            var cardData = {
                'name': $('#name').val()
            };
            stripe.createToken(card, cardData).then(function(result) {
                console.log(result);
                if(result.error && result.error.message){
                    alert(result.error.message);
                }else{
                    alert(result.token.id);
                }
            });
        });

    });
</script>
</body>
</html>

我只在 Firefox、Chrome 和 Android 上的 Chrome 中进行了测试。似乎工作正常。如果您遇到任何问题,请告诉我。

可选的基于 Vue.js 的示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Stripe.js v3 with Bootstrap 4 and Vue.js</title>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css"/>
    <style>
        /* This background color not essential for the example */
        html, body {
            background:#999;
        }

        /* Padding for Stripe Element containers */
        .stripe-element-container {
            padding-top: .55rem;
            padding-bottom: .50rem;
        }

        /* Blue outline on focus */
        .StripeElement--focus {
            border-color: #80BDFF;
            outline:0;
            box-shadow: 0 0 0 .2rem rgba(0,123,255,.25);
            transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
        }

        /* Can't see what I type without this */
        #card-number.form-control,
        #card-cvc.form-control,
        #card-exp.form-control {
            display:inline-block;
        }
    </style>
</head>
<body>
    <div id="app">
        <stripe-form inline-template>
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-4 offset-md-4 pt-5">
                        <div class="card">
                            <div class="card-header">
                                <h3 class="mb-0">Pay Now</h3>
                            </div>
                            <div class="card-body">
                                <div v-bind:class="{alert: activeError, 'alert-danger': activeError}" role="alert" v-html="errorText"></div>

                                <form>

                                    <div class="form-group mb-4">
                                        <label for="name">Name on Card</label>
                                        <input type="text" class="form-control" v-model="ccName" />
                                    </div>

                                    <div class="form-group">
                                        <label for="card-number">Credit Card Number</label>
                                        <span id="card-number" class="form-control stripe-element-container">
                                            <!-- Stripe Card Element -->
                                        </span>
                                    </div>

                                    <div class="form-group">
                                        <label for="card-cvc">CVC Number</label>
                                        <span id="card-cvc" class="form-control stripe-element-container">
                                            <!-- Stripe CVC Element -->
                                        </span>
                                    </div>

                                    <div class="form-group">
                                        <label for="card-exp">Expiration</label>
                                        <span id="card-exp" class="form-control stripe-element-container">
                                            <!-- Stripe Card Expiry Element -->
                                        </span>
                                    </div>

                                    <button @click.prevent="paymentSubmit" class="btn btn-primary mt-1 float-right">Submit Payment</button>

                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </stripe-form>
        <modal></modal>
    </div>

<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script src="https://unpkg.com/babel-polyfill@latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>
<script src="https://js.stripe.com/v3/"></script>
<script>

// Your Stripe public key
const stripePublicKey = 'pk_test_XxXxXxXxXxXxXxXxXxXxXxXx';

/**
 * Class allows firing of events and 
 * listening of events between components
 */
window.Events = new class {

    constructor(){
        this.vue = new Vue();
    }

    fire( event, data = null ){
        this.vue.$emit( event, data );
    }

    listenFor( event, callback ){
        this.vue.$on( event, callback );
    }
}

/**
 * See: https://bootstrap-vue.js.org/docs/components/modal/
 */
Vue.component('modal', {

    template: `
        <div>
            <b-modal ref="myModalRef" ok-only ok-title="Close" v-bind:title="title">
                <p class="mb-0">{{ body }}</p>
            </b-modal>
        </div>
    `,

    data: function(){
        return {
            title: '',
            body: ''
        }
    },

    methods: {
        showModal () {
          this.$refs.myModalRef.show()
        }
        /* This not needed for this example
        ,
        hideModal () {
          this.$refs.myModalRef.hide()
        }
        */
    },

    created(){
        Events.listenFor('modalShow', ( data ) => {
            this.title = data.title;
            this.body = data.message;
            this.showModal();
        });
    }

});

Vue.component('stripe-form', {

    data: function(){
        return { 
            activeError: false,
            errorText: '',
            ccName: '',
            stripe: null,
            card: null,
            cvc: null,
            exp: null
        }
    },

    methods: {
        paymentSubmit: function(){
            let cardData = {
                'name': this.ccName
            };
            // Ensure the name field is not empty
            if( cardData.name.trim() == '' ){
                // Show an error
                this.activeError = true;
                this.errorText = '<b>Submission Error:</b><br />Name is required.';
                // Abort !!
                return;
            }
            this.stripe.createToken( this.card, cardData).then( (result) => {
                if(result.error && result.error.message){

                    // Show any errors
                    this.activeError = true;
                    this.errorText = '<b>Submission Error:</b><br />' + result.error.message;

                }else{

                    /**
                     * Success message in modal.
                     * This is normally where you'd post to your server, 
                     * and have it actually attempt the credit card transaction
                     * using the token ID that was just received.
                     */
                    Events.fire('modalShow', {
                        'title': 'Success',
                        'message': result.token.id
                    });

                    // Clear the form
                    this.activeError = false;
                    this.errorText = '';
                    this.ccName = '';
                    // Stripe elements must be cleared in a special way
                    this.card.clear();
                    this.cvc.clear();
                    this.exp.clear();
                }
            });
        }
    },

    mounted: function(){
        // Create a Stripe client
        this.stripe = Stripe( stripePublicKey );

        // Create an instance of Elements
        const elements = this.stripe.elements();

        /**
         * Try to match bootstrap 4 styling.
         * --------------------------------
         * fontSize was in rem units, but Stripe says that it should be in pixels.
         */
        const style = {
            base: {
                'fontSize': '16px',
                'color': '#495057',
                'fontFamily': 'apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'
            }
        };

        // Card number
        this.card = elements.create('cardNumber', {
            'placeholder': '',
            'style': style
        });
        this.card.mount('#card-number');

        // CVC
        this.cvc = elements.create('cardCvc', {
            'placeholder': '',
            'style': style
        });
        this.cvc.mount('#card-cvc');

        // Card expiry
        this.exp = elements.create('cardExpiry', {
            'placeholder': '',
            'style': style
        });
        this.exp.mount('#card-exp');
    }

});

new Vue({ el: '#app' });

</script>
</body>
</html>

这个 Vue.js 示例可以从一些工作中受益,但可能会帮助您入门。

于 2017-09-07T03:56:44.060 回答
52

在对文档进行了更多挖掘之后,我发现 https://stripe.com/docs/stripe.js#the-element-container说“您应该为您安装元素的容器设置样式,就好像它是您的页。”

通过将 Bootstrap 的form-control类添加到<div>我正在安装的元素中,该字段看起来几乎与任何其他 Bootstrap 样式的输入字段一样:

<div id="card-element" class="form-control"></div>

由于某种原因,场地的高度不太匹配,但通过反复试验,我得到了它:

var stripe = Stripe('your_key');
var elements = stripe.elements();
var card = elements.create('card', { style:
  {
    base: {
      lineHeight: '1.429'
    }
  }
});
card.mount('#card-element');
于 2017-09-04T14:35:39.200 回答
30

这就是我们使用所需的全部内容

  <div class="form-group">
    <label for="card-element">Credit or debit card</label>
    <div id="card-element" class="form-control" style='height: 2.4em; padding-top: .7em;'>
      <!-- A Stripe Element will be inserted here. -->
    </div>
  </div>

在此处输入图像描述

于 2018-11-02T20:53:39.060 回答
9

添加另一个答案,因为我觉得这里最简单的答案有点不完整。添加了错误显示和用于将令牌发送到后端的 JavaScript,以防您想要这样做。

这个 HTML(来自他们的默认文档,添加了 Bootstrap 4 元素)

<form action="/charge" method="post" id="payment-form">
  <div class="form-group">
    <label for="card-element">
        Credit or debit card
    </label>
    <div id="card-element" class="form-control">
        <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display Element errors. -->
    <div id="card-errors" role="alert"></div>
  </div>

  <button>Submit Payment</button>
</form>

这是 JavaScript(同样来自他们的默认文档,但我在这里使用 jQuery,但您不必这样做)

<script src="https://js.stripe.com/v3/"></script>
<script>
(function ($) {
    // Set your publishable key: remember to change this to your live publishable key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    var stripe = Stripe('yourPublishableKeyHere');
    var elements = stripe.elements();

    // Custom styling can be passed to options when creating an Element.
    var style = {
      base: {
        // Add your base input styles here. For example:
        fontSize: '16px',
        color: '#32325d',
      },
    };

    // Create an instance of the card Element.
    var card = elements.create('card', {style: style});

    // Add an instance of the card Element into the `card-element` <div>.
    card.mount('#card-element');


    // Create a token or display an error when the form is submitted.
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(event) {
      event.preventDefault();

      stripe.createToken(card).then(function(result) {
        if (result.error) {
          // Inform the customer that there was an error.
          var errorElement = document.getElementById('card-errors');
          errorElement.textContent = result.error.message;
        } else {
          // Send the token to your server.
          stripeTokenHandler(result.token);
        }
      });
    });

    function stripeTokenHandler(token) {
      // Insert the token ID into the form so it gets submitted to the server
      var form = document.getElementById('payment-form');
      var hiddenInput = document.createElement('input');
      hiddenInput.setAttribute('type', 'hidden');
      hiddenInput.setAttribute('name', 'stripeToken');
      hiddenInput.setAttribute('value', token.id);
      form.appendChild(hiddenInput);

      // Submit the form
      form.submit();
    }
})(jQuery);
</script>
于 2020-02-03T18:32:03.477 回答
1

我正在使用带反应的条纹,我试图在父容器上使用类,但它对我不起作用。所以,我最终为容器使用了内联样式。这是对我有用的最终代码。

const containerStyles = {
border: '1px solid #d3d3d3',
padding: '5px 10px 5px 10px',
borderRadius: '4px',
width: '100%', };

const cardNumberElementOptions = {
style: {
  base: {
    fontSize: '14px',
    lineHeight: '45px',
  },
},
placeholder: 'Card Number',
};

<div style={containerStyles}>
      <CardNumberElement options={cardNumberElementOptions} />
</div>
于 2021-12-15T11:23:40.627 回答
1

改进其他答案,我得到了以下解决方案。不过,带上一粒盐。.StripeElement--focus和的 CSS.card-element-disabled是从 Bootstrap 复制的,因此您可能希望从您正在使用的版本中获取它们(我认为我有 4.4)。

另外,我没有复制支付逻辑,因为它特定于我的应用程序,但是disable_form当按下按钮并且 JavaScript 开始处理内容enable_form时,以及 JavaScript 完成并且可以重新启用表单时(可能是因为有一个错误,否则您只需转到另一个页面)。

<style>
  .StripeElement--focus {
      color: #495057;
      background-color: #fff;
      border-color: #80bdff;
      outline: 0;
      box-shadow: 0 0 0 .2rem rgba(0,123,255,.25);
  }
  .card-element-disabled {
      background-color: #e9ecef;
      opacity: 1;
  }
  #card-element {
      display: flex;
      align-items: center;
  }
  #card-element div {
      flex-grow: 1;
  }
  #card-element-error {
      color: #ff0000;
  }
</style>
<script>
  function disable_form() {
      $(".form-control").prop('disabled', true);
      card.update({disabled: true});
      $("#submit-btn").prop('disabled', true).html("Loading");
      $("#card-element").addClass('card-element-disabled');
  }

  function enable_form() {
      $(".form-control").prop('disabled', false);
      card.update({disabled: false});
      $("#submit-btn").prop('disabled', false).html("Submit");
      $("#card-element").removeClass('card-element-disabled');
  }

  var stripe = Stripe('{{ stripe_publishable_key }}');
  var elements = stripe.elements();
  var card = elements.create('card');

  function setup_stripe() {
      card.mount('#card-element');
  }

  setTimeout(setup_stripe, 0);
</script>

<form id="payment-form">
    <!-- all other fields -->

    <div class="form-group">
      <label for="card-element">Card number</label>
      <div id="card-element" class="form-control"></div>
    </div>

    <div id="card-element-error" role="alert"></div>
    <button type="submit" class="btn btn-primary" id="submit-btn">Submit</button>
</form>
于 2021-04-18T08:29:10.903 回答