Learn how to store, update, and use metadata on Stripe charges to enhance payment tracking, reporting, and integration with your systems. Step-by-step guide.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
How to Store Metadata on Stripe Charges: A Comprehensive Tutorial
Storing metadata on Stripe charges can significantly enhance your payment tracking capabilities by attaching custom information to transactions. This guide will walk you through the process in detail.
Step 1: Set Up Your Stripe Account and API Keys
Before you can store metadata on charges, you need to set up your Stripe account and obtain your API keys:
// Example of initializing Stripe with your secret key
const stripe = require('stripe')('sk_test_your_secret_key');
Step 2: Understand Metadata in Stripe
Metadata in Stripe is a key-value store that allows you to attach custom data to various Stripe objects, including charges. Important characteristics:
Step 3: Adding Metadata When Creating a Charge
Here's how to include metadata when creating a new charge:
const stripe = require('stripe')('sk_test_your_secret_key');
async function createChargeWithMetadata() {
try {
const charge = await stripe.charges.create({
amount: 2000, // Amount in cents
currency: 'usd',
source: 'tok\_visa', // Or a customer ID or payment method ID
description: 'My first charge with metadata',
metadata: {
order\_id: '6735',
customer\_name: 'John Doe',
product_id: 'prod_123456',
shipment\_date: '2023-05-15',
custom\_field: 'Any additional information'
}
});
console.log('Charge created with ID:', charge.id);
console.log('Metadata:', charge.metadata);
return charge;
} catch (error) {
console.error('Error creating charge:', error);
throw error;
}
}
createChargeWithMetadata();
Step 4: Adding Metadata When Creating a PaymentIntent
For modern Stripe integrations using Payment Intents (recommended):
const stripe = require('stripe')('sk_test_your_secret_key');
async function createPaymentIntentWithMetadata() {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: 2000,
currency: 'usd',
payment_method_types: ['card'],
description: 'Payment intent with metadata',
metadata: {
order\_id: '6735',
customer\_name: 'John Doe',
product_id: 'prod_123456',
shipping\_method: 'express',
promotion\_code: 'SUMMER20'
}
});
console.log('PaymentIntent created with ID:', paymentIntent.id);
console.log('Metadata:', paymentIntent.metadata);
return paymentIntent;
} catch (error) {
console.error('Error creating payment intent:', error);
throw error;
}
}
createPaymentIntentWithMetadata();
Step 5: Updating Metadata on an Existing Charge
If you need to update metadata after a charge has been created:
const stripe = require('stripe')('sk_test_your_secret_key');
async function updateChargeMetadata(chargeId) {
try {
const charge = await stripe.charges.update(
chargeId,
{
metadata: {
status: 'shipped',
tracking\_number: '1Z999AA10123456784',
updated\_at: new Date().toISOString()
}
}
);
console.log('Charge updated with ID:', charge.id);
console.log('Updated metadata:', charge.metadata);
return charge;
} catch (error) {
console.error('Error updating charge:', error);
throw error;
}
}
updateChargeMetadata('ch\_123456789');
Step 6: Using Metadata with Checkout Sessions
If you're using Stripe Checkout, you can add metadata to the session:
const stripe = require('stripe')('sk_test_your_secret_key');
async function createCheckoutSessionWithMetadata() {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line\_items: [
{
price\_data: {
currency: 'usd',
product\_data: {
name: 'T-shirt',
},
unit\_amount: 2000,
},
quantity: 1,
},
],
mode: 'payment',
success\_url: 'https://example.com/success',
cancel\_url: 'https://example.com/cancel',
metadata: {
order\_id: '6735',
customer_id: 'cust_123456',
source: 'summer\_campaign',
referral\_code: 'REF789'
}
});
console.log('Checkout Session created with ID:', session.id);
console.log('Metadata:', session.metadata);
return session;
} catch (error) {
console.error('Error creating checkout session:', error);
throw error;
}
}
createCheckoutSessionWithMetadata();
Step 7: Retrieving and Accessing Metadata
To retrieve a charge and access its metadata:
const stripe = require('stripe')('sk_test_your_secret_key');
async function getChargeMetadata(chargeId) {
try {
const charge = await stripe.charges.retrieve(chargeId);
console.log('Charge ID:', charge.id);
console.log('All metadata:', charge.metadata);
// Access specific metadata fields
const orderId = charge.metadata.order\_id;
const customerName = charge.metadata.customer\_name;
console.log('Order ID:', orderId);
console.log('Customer Name:', customerName);
return charge.metadata;
} catch (error) {
console.error('Error retrieving charge:', error);
throw error;
}
}
getChargeMetadata('ch\_123456789');
Step 8: Searching Charges Using Metadata
You can search for charges based on their metadata:
const stripe = require('stripe')('sk_test_your_secret_key');
async function searchChargesByMetadata() {
try {
const charges = await stripe.charges.list({
limit: 10,
// Search for charges with a specific order\_id in metadata
search: {
query: 'metadata["order\_id"]:"6735"'
}
});
console.log('Found charges:', charges.data.length);
charges.data.forEach(charge => {
console.log('Charge ID:', charge.id);
console.log('Amount:', charge.amount / 100); // Convert cents to dollars
console.log('Metadata:', charge.metadata);
console.log('------------------------');
});
return charges.data;
} catch (error) {
console.error('Error searching charges:', error);
throw error;
}
}
searchChargesByMetadata();
Step 9: Handling Metadata in Webhooks
When receiving webhook events from Stripe, you can access the metadata to process the event:
const express = require('express');
const stripe = require('stripe')('sk_test_your_secret_key');
const app = express();
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.path === '/webhook') {
next();
} else {
express.json()(req, res, next);
}
});
// Special handling for webhook endpoint
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
// Construct the event from the raw request and signature
event = stripe.webhooks.constructEvent(
req.body,
sig,
'whsec_your_webhook_signing_secret'
);
} catch (err) {
console.log(`Webhook Error: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
if (event.type === 'charge.succeeded') {
const charge = event.data.object;
const metadata = charge.metadata;
console.log('Charge succeeded with ID:', charge.id);
console.log('Charge metadata:', metadata);
// Use metadata to process your business logic
if (metadata.order\_id) {
// Update order status in your database
console.log(`Updating order ${metadata.order_id} to paid status`);
// await updateOrderStatus(metadata.order\_id, 'paid');
}
if (metadata.customer\_id) {
// Update customer payment records
console.log(`Recording payment for customer ${metadata.customer_id}`);
// await recordCustomerPayment(metadata.customer\_id, charge.amount);
}
}
// Return a 200 response to acknowledge receipt of the event
res.send({received: true});
});
app.listen(3000, () => console.log('Running on port 3000'));
Step 10: Best Practices for Using Metadata
To make the most of Stripe metadata:
Example of storing a more complex data structure:
const orderDetails = {
items: [
{ id: 'SKU123', quantity: 2, price: 10.99 },
{ id: 'SKU456', quantity: 1, price: 24.99 }
],
shipping\_address: {
city: 'San Francisco',
state: 'CA',
country: 'US'
},
discount\_applied: true
};
// Store as JSON string in metadata
const charge = await stripe.charges.create({
amount: 4697, // 46.97 in cents
currency: 'usd',
source: 'tok\_visa',
metadata: {
order\_id: '6735',
order\_details: JSON.stringify(orderDetails)
}
});
// Later, parse it back to an object
const retrievedCharge = await stripe.charges.retrieve('ch\_123456789');
const parsedOrderDetails = JSON.parse(retrievedCharge.metadata.order\_details);
console.log('Items ordered:', parsedOrderDetails.items.length);
Step 11: Implementing Metadata in Different Programming Languages
Here are examples of implementing metadata in different programming languages:
Python:
import stripe
stripe.api_key = 'sk_test_your_secret\_key'
# Create a charge with metadata
charge = stripe.Charge.create(
amount=2000,
currency='usd',
source='tok\_visa',
description='Charge with metadata in Python',
metadata={
'order\_id': '6735',
'customer\_name': 'John Doe',
'product_id': 'prod_123456'
}
)
print(f"Charge created with ID: {charge.id}")
print(f"Metadata: {charge.metadata}")
# Update metadata
updated\_charge = stripe.Charge.modify(
charge.id,
metadata={
'status': 'shipped',
'tracking\_number': '1Z999AA10123456784'
}
)
print(f"Updated metadata: {updated\_charge.metadata}")
PHP:
2000,
'currency' => 'usd',
'source' => 'tok\_visa',
'description' => 'Charge with metadata in PHP',
'metadata' => [
'order\_id' => '6735',
'customer\_name' => 'John Doe',
'product_id' => 'prod_123456'
]
]);
echo "Charge created with ID: " . $charge->id . "\n";
echo "Metadata: ";
print\_r($charge->metadata);
// Update metadata
$updated\_charge = \Stripe\Charge::update(
$charge->id,
[
'metadata' => [
'status' => 'shipped',
'tracking\_number' => '1Z999AA10123456784'
]
]
);
echo "Updated metadata: ";
print_r($updated_charge->metadata);
} catch (\Stripe\Exception\ApiErrorException $e) {
echo "Error: " . $e->getMessage();
}
?>
Ruby:
require 'stripe'
Stripe.api_key = 'sk_test_your_secret\_key'
begin
# Create a charge with metadata
charge = Stripe::Charge.create({
amount: 2000,
currency: 'usd',
source: 'tok\_visa',
description: 'Charge with metadata in Ruby',
metadata: {
order\_id: '6735',
customer\_name: 'John Doe',
product_id: 'prod_123456'
}
})
puts "Charge created with ID: #{charge.id}"
puts "Metadata: #{charge.metadata}"
# Update metadata
updated\_charge = Stripe::Charge.update(
charge.id,
metadata: {
status: 'shipped',
tracking\_number: '1Z999AA10123456784'
}
)
puts "Updated metadata: #{updated\_charge.metadata}"
rescue Stripe::StripeError => e
puts "Error: #{e.message}"
end
Step 12: Integrating Metadata with Your Database
To maintain a connection between Stripe charges and your internal database:
const stripe = require('stripe')('sk_test_your_secret_key');
const { Pool } = require('pg'); // Using PostgreSQL for this example
// Create a database connection pool
const pool = new Pool({
user: 'db\_user',
host: 'localhost',
database: 'my\_app',
password: 'password',
port: 5432,
});
async function processOrder(orderId, customerId, amount, productIds) {
const client = await pool.connect();
try {
// Start a transaction
await client.query('BEGIN');
// Create the charge in Stripe with metadata
const charge = await stripe.charges.create({
amount: amount \* 100, // Convert to cents
currency: 'usd',
source: 'tok\_visa', // In real scenario, you would use a saved payment method
description: `Order #${orderId}`,
metadata: {
order\_id: orderId.toString(),
customer\_id: customerId.toString(),
products: productIds.join(',')
}
});
// Save the charge information in your database
const saveChargeResult = await client.query(
'INSERT INTO payments (order_id, customer_id, stripe_charge_id, amount, status) VALUES ($1, $2, $3, $4, $5) RETURNING id',
[orderId, customerId, charge.id, amount, 'successful']
);
const paymentId = saveChargeResult.rows[0].id;
// Update order status
await client.query(
'UPDATE orders SET status = $1, payment_id = $2, updated_at = NOW() WHERE id = $3',
['paid', paymentId, orderId]
);
// Commit the transaction
await client.query('COMMIT');
console.log(`Order ${orderId} processed successfully with Stripe charge ${charge.id}`);
return { success: true, chargeId: charge.id, paymentId };
} catch (error) {
// If anything fails, roll back the transaction
await client.query('ROLLBACK');
console.error('Error processing order:', error);
return { success: false, error: error.message };
} finally {
// Release the client back to the pool
client.release();
}
}
// Example usage
processOrder(6735, 12345, 49.99, ['prod_123', 'prod_456'])
.then(result => console.log(result))
.catch(error => console.error(error));
Step 13: Building a Reporting System Using Metadata
Use metadata to build a custom reporting system:
const stripe = require('stripe')('sk_test_your_secret_key');
const express = require('express');
const app = express();
app.use(express.json());
// API endpoint to generate a report based on metadata
app.get('/api/reports/sales-by-product', async (req, res) => {
try {
const startDate = req.query.start\_date || '2023-01-01';
const endDate = req.query.end\_date || new Date().toISOString().split('T')[0];
// Convert dates to timestamps for Stripe API
const startTimestamp = Math.floor(new Date(startDate).getTime() / 1000);
const endTimestamp = Math.floor(new Date(endDate).getTime() / 1000);
// Get all charges in the date range
const charges = await stripe.charges.list({
limit: 100,
created: {
gte: startTimestamp,
lte: endTimestamp
}
});
// Group and count by product\_id in metadata
const productSales = {};
let totalSales = 0;
charges.data.forEach(charge => {
if (charge.status === 'succeeded' && charge.metadata && charge.metadata.product\_id) {
const productId = charge.metadata.product\_id;
const amount = charge.amount / 100; // Convert cents to dollars
if (!productSales[productId]) {
productSales[productId] = {
count: 0,
revenue: 0
};
}
productSales[productId].count += 1;
productSales[productId].revenue += amount;
totalSales += amount;
}
});
// Format the results
const results = {
start\_date: startDate,
end\_date: endDate,
total\_sales: totalSales.toFixed(2),
products: Object.keys(productSales).map(productId => ({
product\_id: productId,
sales\_count: productSales[productId].count,
revenue: productSales[productId].revenue.toFixed(2)
}))
};
res.json(results);
} catch (error) {
console.error('Error generating report:', error);
res.status(500).json({ error: 'Failed to generate report' });
}
});
app.listen(3000, () => console.log('Reporting server running on port 3000'));
Conclusion
Stripe's metadata feature provides a powerful way to associate custom data with your charges and payment intents. By following this comprehensive tutorial, you can implement a robust system for tracking, querying, and managing payments with your application-specific data. Remember to use metadata strategically, keeping your keys and values consistent and meaningful to maximize the benefits of this flexible system.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.