Stripe Issuing

Overview

Stripe Issuing lets you create virtual cards that can be used with the Mercoa Virtual Card Agent to process invoice payments securely.

Integration Setup

Enable Stripe Issuing in Stripe Account

To start using Stripe Issuing, ensure your Stripe account has it enabled:

  1. Log in to your Stripe Dashboard.
  2. Click Issuing from the left-hand menu.
  3. Complete the Issuing application process.
  4. Locate and save your publishable_key and secret_key.

Create Virtual Card

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

  • Use ephemeral keys that expire quickly (typically within 1 hour) and are scoped to a single operation.
  • Avoid storing card numbers. Your backend must not store or process full card data.
  • Ensure PCI compliance by using Stripe’s iframe-based tokenization and minimal card data handling.
  • Enable logging for all virtual card operations to maintain an audit trail for compliance and traceability.

Best Practices

🔐 Security

  • Use one-time virtual cards with exact amounts.
  • Set strict spending limits per card.
  • Avoid storing card numbers directly.
  • Monitor card usage in the Stripe Dashboard.

📊 Reconciliation

  • Store transaction IDs from Stripe with invoice metadata.
  • Use metadata to associate cards with invoices.
  • Set up webhooks to track card lifecycle and usage.