# Using Issuing Elements Learn how to display card details in your web application in a PCI-compliant way. [Stripe.js](https://docs.stripe.com/js) includes a browser-side JavaScript library you can use to display the sensitive data of your Issuing cards on the web in compliance with PCI requirements. The sensitive data renders inside Stripe-hosted iframes and never touches your servers. Stripe.js collects extra data to protect our users. Learn more about how Stripe collects data for [advanced fraud detection](https://docs.stripe.com/disputes/prevention/advanced-fraud-detection.md). ## Ephemeral key authentication Stripe.js uses ephemeral keys to securely retrieve Card information from the Stripe API without publicly exposing your secret keys. You need to do some of the ephemeral key exchange on the server-side to set this up. The ephemeral key creation process begins in the browser, by creating a **nonce** using Stripe.js. A nonce is a single-use token that creates an **ephemeral key**. This nonce is sent to your server, where you exchange it for an ephemeral key by calling the Stripe API (using your secret key). After creating an ephemeral key server-side, pass it back to the browser for Stripe.js to use. ## Create a secure endpoint The first step to integrating with Issuing Elements is to create a secure, server-side endpoint to generate ephemeral keys for the card you want to show. Your Issuing Elements web integration calls this endpoint. Here’s how you might implement an ephemeral key creation endpoint in web applications framework across various languages: ```javascript // This example sets up an endpoint using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.urlencoded({ extended: true })); const stripe = require('stripe')('<>'); app.post('/ephemeral-keys', async (request, response) => { const { card_id, nonce } = request.body; const ephemeralKey = await stripe.ephemeralKeys.create({ nonce: nonce, issuing_card: card_id, }, { apiVersion: '{{merchant.apiVersion}}', }); response.json({ ephemeralKeySecret: ephemeralKey.secret, }); }); ``` ```ruby \# This example sets up an endpoint using the Sinatra framework. # Watch this video to get started: https://youtu.be/8aA9Enb8NVc require 'sinatra' require 'sinatra/json' Stripe.api_key = '<>' post '/ephemeral-keys' do @ephemeral_key = Stripe::EphemeralKey.create({ nonce: params[:nonce], issuing_card: params[:card_id], }, { stripe_version: '{{merchant.apiVersion}}', }) json(ephemeralKeySecret: @ephemeral_key.secret) end ``` ```php >'); $payload = @file_get_contents('php://input'); $params = json_decode($payload, true); try { $ephemeralKey = $stripe->ephemeralKeys->create([ 'nonce' => $params['nonce'], 'issuing_card' => $params['card_id'], ], [ 'stripe_version' => '2022-08-01' ]); } catch(\UnexpectedValueException $e) { http_response_code(400); exit(); } catch(\Stripe\Exception\SignatureVerificationException $e) { // Invalid signature http_response_code(400); exit(); } http_response_code(200); echo json_encode(['ephemeralKeySecret' => $ephemeralKey->secret]); ``` ```python \# This example sets up an endpoint using the Flask framework. # Watch this video to get started: https://youtu.be/7Ul1vfmsDck import stripe from flask import Flask, request, jsonify app = Flask(__name__) stripe.api_key = '<>' @app.route('/ephemeral-keys', methods=['POST']) def ephemeral_keys(): ephemeralKey = stripe.EphemeralKey.create( nonce=request.args.get('nonce'), issuing_card=request.args.get('card_id'), stripe_version='{{merchant.apiVersion}}', ) return jsonify( ephemeralKeySecret=ephemeralKey.secret, ) ``` You must specify the API version when creating ephemeral keys. Currently, the required version is `2020-03-02`. You must also pass in an ephemeral key nonce, which you can create in your web integration. ## Web API integration First, include Stripe.js on your page. For more information on how to set up Stripe.js, refer to [including Stripe.js.](https://docs.stripe.com/js/including) Create a `Stripe` instance and an ephemeral key nonce for the card you want to retrieve using [stripe.createEphemeralKeyNonce](https://docs.stripe.com/js/issuing/create_ephemeral_key_nonce). Use the nonce to retrieve the ephemeral key by calling the [server-side endpoint](#create-secure-endpoint) that you created: ```javascript const stripe = Stripe('<>'); // Initialize Elements which you'll need later const elements = stripe.elements(); // Use Stripe.js to create a nonce const cardId = 'ic_1ITi6XKYfU8ZP6raDAXem8ql'; const nonceResult = await stripe.createEphemeralKeyNonce({ issuingCard: cardId, }); const nonce = nonceResult.nonce; // Call your ephemeral key creation endpoint to fetch the ephemeral key const ephemeralKeyResult = await fetch('/ephemeral-keys', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ card_id: cardId, nonce: nonce, }) }); const ephemeralKeyResponse = await ephemeralKeyResult.json(); const ephemeralKeySecret = ephemeralKeyResponse.ephemeralKeySecret; ``` Now that you have an ephemeral key, you’re ready to show sensitive card details. You can do so using any of the following Elements, and you can re-use the same nonce and ephemeral key pair for multiple Elements on the same page: | Element | Name | Availability | | ------------ | -------------------------- | ------------------- | | Number (PAN) | `issuingCardNumberDisplay` | Virtual cards only | | CVC | `issuingCardCvcDisplay` | Virtual cards only | | Expiry date | `issuingCardExpiryDisplay` | Virtual cards only | | PIN | `issuingCardPinDisplay` | Physical cards only | Each Element takes the following configuration: | Name | Type | Usage | | -------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `style` | [Style object](https://docs.stripe.com/js/appendix/style) | Keep in mind that some variants, pseudo-classes, and properties are for input Elements and won’t apply to these Elements. An example of an input-only pseudo-class is `::placeholder`. | | `issuingCard` | `string` | The ID of your issued card (for example, `ic_abc123`) | | `nonce` | `string` | Your ephemeral key nonce | | `ephemeralKeySecret` | `string` | The `secret` component of your ephemeral key | If you decide to use `issuingCardPinDisplay`, then you must implement appropriate methods to ensure that access is limited to your authorized users. In particular, you must apply two-factor authentication (2FA) before providing access to a page using `issuingCardPinDisplay`. If Stripe decides that you don’t have sufficient security measures in place, we might suspend your access to this Element. The following is an example of how to display one of these Elements, using the nonce and ephemeral key pair created in the example above: ```javascript const number = elements.create('issuingCardNumberDisplay', { issuingCard: cardId, nonce: nonce, ephemeralKeySecret: ephemeralKeySecret, style: { base: { color: '#fff', fontSize: '16px' }, }, }); number.mount('#card-number'); ``` ## Adding a copy button In addition to the “card data display elements” that we’ve already described, we also provide an `issuingCardCopyButton` element. This takes a `toCopy` argument and renders a transparent “copy to clipboard” button that takes up the space of its parent `
`. This allows it to intercept all click events with a click handler that takes the corresponding card data specified at initialization and copies it to the clipboard. With this, you can display “copy to clipboard” buttons next to the card number, expiry, and cvc, which prevents your cardholders from manually copying card data. We restrict the copy functionality to Stripe’s PCI-compliant `