Stripe Issuing

Overview

Stripe Issuing allows you to create virtual cards programmatically for use with the Virtual Card Agent.

Integration Setup

Stripe Account Configuration

Ensure your Stripe account has Issuing enabled:

  1. Navigate to your Stripe Dashboard
  2. Go to Issuing in the left sidebar
  3. Complete the application process if not already done
  4. Note your publishable_key and secret_key

Virtual Card Creation

Create virtual cards for specific invoices:

1const virtualCard = await stripe.issuing.cards.create({
2 cardholder: cardholder.id,
3 currency: 'usd',
4 status: 'active',
5 type: 'virtual',
6 spending_controls: {
7 spending_limits: [
8 {
9 interval: 'all_time',
10 amount: invoice.amount, // amount must be an integer in the currency’s smallest unit* (cents for USD)
11 }
12 ],
13 },
14 metadata: {
15 invoice_id: invoice.id,
16 vendor_id: invoice.vendor_id,
17 },
18});

This is just an example, please refer to the Stripe Issuing documentation for more information.

API Integration

The Stripe Issuing integration with the Virtual Card Agent provides a secure, automated workflow for processing virtual card payments. This integration uses ephemeral keys and iFrame technology to ensure sensitive card data is handled securely.

How It Works

The integration follows a secure workflow where your Stripe virtual card is used to process payments through the Virtual Card Agent:

Process Flow:

  1. Create a Stripe virtual card with spending controls matching the invoice amount
  2. Call the Mercoa API with your card ID and ephemeral key endpoint configuration
  3. The agent uses your backend to generate a temporary ephemeral key
  4. Secure card data is retrieved through Stripe’s iFrame technology
  5. The agent completes the payment through the vendor’s payment gateway
  6. Receipt and confirmation details are captured for reconciliation

API Request Structure

When using Stripe Issuing with the Virtual Card Agent, your API request should include:

1{
2 "type": "html",
3 "html": "<html><body><h1>Invoice Details</h1><a href=\"https://www.payment-gateway.com/invoice/123123\">Pay Invoice</a></body></html>",
4 "cardDetails": {
5 "type": "stripeIssuing",
6 "firstName": "John",
7 "lastName": "Doe",
8 "postalCode": "12345",
9 "country": "US",
10 "stripeCardId": "ic_1234567890abcdef",
11 "stripePublishableKey": "pk_test_1234567890abcdef",
12 "ephemeralKeyEndpoint": {
13 "url": "https://api.example.com/ephemeral-keys",
14 "method": "POST",
15 "headers": {
16 "Authorization": "Bearer YOUR_AUTH_SCHEME",
17 "Content-Type": "application/json"
18 },
19 "postBody": "{\"card_id\": \"{{cardId}}\", \"nonce\": \"{{nonce}}\", \"account_id\": \"{{accountId}}\"}"
20 }
21 }
22}

Field Descriptions

Card Details Object

FieldTypeRequiredDescription
typestringYesMust be "stripeIssuing"
firstNamestringYesCardholder’s first name
lastNamestringYesCardholder’s last name
postalCodestringYesBilling address postal code
countrystringYesBilling address country (ISO code)
stripeAccountIdstringNoStripe account ID of connected account (required for Stripe Connect)
stripeCardIdstringYesStripe Issuing card ID (starts with ic_)
stripePublishableKeystringYesYour Stripe publishable key
ephemeralKeyEndpointobjectYesConfiguration for ephemeral key generation

Ephemeral Key Endpoint

FieldTypeRequiredDescription
urlstringYesYour backend endpoint for generating ephemeral keys
methodstringNoHTTP method (defaults to POST)
headersobjectYesHTTP headers for the request (template with variables)
postBodystringYesRequest body (template with variables)

Supported Variables

The postBody and headers template supports these variables that will be replaced with actual values:

  • {{cardId}} - The Stripe card ID
  • {{nonce}} - A unique nonce for this request
  • {{accountId}} - Your Stripe account ID (if applicable)

Backend Implementation

Your backend needs to implement an endpoint that generates ephemeral keys. Here’s an example:

1app.post('/ephemeral-keys', async (req, res) => {
2 const { card_id, nonce, account_id } = req.body;
3
4 try {
5 const ephemeralKey = await stripe.ephemeralKeys.create(
6 {
7 issuing_card: card_id,
8 nonce: nonce,
9 },
10 {
11 apiVersion: '2023-10-16', // Use latest API version
12 }
13 );
14
15 res.json({
16 ephemeralKeySecret: ephemeralKey.secret
17 });
18 } catch (error) {
19 res.status(400).json({
20 error: 'Failed to create ephemeral key'
21 });
22 }
23});

Ephemeral Key Response Structure

Your ephemeral key endpoint must return the ephemeral key secret in one of these formats:

Option 1: Object with ephemeralKeySecret property

1{
2 "ephemeralKeySecret": "ek_live_1234567890abcdef..."
3}

Option 2: Object with secret property

1{
2 "secret": "ek_live_1234567890abcdef..."
3}

Option 3: Plain string (the ephemeral key secret directly)

"ek_live_1234567890abcdef..."

The ephemeral key secret must be a valid Stripe ephemeral key that was created for the specific card and nonce provided in the request.

Security Considerations

  • Ephemeral Keys: These keys expire quickly (typically 1 hour) and can only be used for specific operations
  • No Card Data Storage: Your backend never stores or processes actual card numbers
  • PCI Compliance: The integration is designed to minimize your PCI compliance burden
  • Audit Trail: All operations are logged for security and compliance purposes

Error Handling

Common error scenarios and how to handle them:

ErrorCauseResolution
INVALID_STRIPE_CARD_IDCard ID format is incorrectVerify the card ID starts with ic_
EPHEMERAL_KEY_FAILEDBackend endpoint failedCheck your ephemeral key endpoint
CARD_NOT_FOUNDCard doesn’t exist or is inactiveVerify card status in Stripe Dashboard
INSUFFICIENT_FUNDSCard spending limit exceededCheck card spending controls

Best Practices

Security

  • Use one-time cards with exact amounts
  • Set appropriate spending limits
  • Monitor card usage through Stripe Dashboard
  • Implement proper error handling

Reconciliation

  • Store Stripe transaction IDs with invoice records
  • Use metadata to link cards to specific invoices
  • Implement webhook handling for real-time updates