Loading...
Loading...
Loading...
Anders & A-Cube S.r.l. / September 1, 2024 • 2 min read
Stripe E-Invoicing Extension is a production Stripe Dashboard UI Extension that provides comprehensive payment document management directly within the Stripe Dashboard. Built with the Stripe UI Extension SDK, it enables seamless invoice validation, refund processing, and synchronization with external accounting systems.
┌──────────────────────────────────────────────────┐
│ Stripe Dashboard Environment │
│ Embedded UI Extension Context │
└────────────────┬─────────────────────────────────┘
│
┌────────────────▼─────────────────────────────────┐
│ Stripe UI Extension SDK Integration │
│ Payment Data • Metadata API • Actions │
└──────┬──────────────────────────────┬────────────┘
│ │
┌──────▼──────────┐ ┌─────────▼────────────┐
│ Redux State │ │ External APIs │
│ - Documents │ │ - A-Cube API │
│ - Focus Views │ │ - Accounting │
│ - Validation │ │ - E-Invoice │
└─────────────────┘ └──────────────────────┘
// Component hierarchy
PaymentDocumentDetailsView
├── DocumentInfo // Basic document information
├── GatewayInfo // Gateway-specific details
├── InvoiceStatus // Status management UI
├── RefundSection // Refund processing
└── MetadataFocusView // Metadata editor modalMain View Component:
import {
ContextView,
usePaymentData,
useMetadata
} from '@stripe/ui-extension-sdk/context'
import { Box, Inline, Banner } from '@stripe/ui-extension-sdk/ui'
export default function PaymentDocumentDetailsView() {
const payment = usePaymentData()
const metadata = useMetadata()
const { document, loading } = useFetchStripeDocuments(payment.id)
if (loading) {
return <LoadingSpinner />
}
return (
<ContextView
Real-time Validation Engine:
import { z } from 'zod'
const InvoiceSchema = z.object({
number: z.string().regex(/^[A-Z]{2}\d{4}-\d{6}$/),
date: z.string().datetime(),
amount: z.number().positive(),
currency: z.enum(['EUR', 'USD', 'GBP']),
taxInfo: z.object({
vatNumber: z.string().regex
Validation Display:
function DocumentInfo({ document }) {
const { errors, isValid } = useValidateDocuments(document)
return (
<Box>
<Inline css={{ alignY: 'center', gap: 'small' }}>
<Badge type={isValid ? 'positive' : 'negative'}>
{isValid ? 'Valid' : `${errors.length} Errors`}
</Badge>
</Inline>
{!isValid
Refund Workflow:
import { useStripe } from '@stripe/ui-extension-sdk/stripe'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
const RefundSchema = z.object({
amount: z.number().positive(),
reason: z.enum(['duplicate', 'fraudulent', 'requested_by_customer']),
notes: z.string().optional()
})
export function RefundSection({ payment, document }) {
const stripe =
Metadata Editor Modal:
import { FocusView } from '@stripe/ui-extension-sdk/ui'
export function MetadataFocusView({ document, onClose }) {
const [metadata, setMetadata] = useState(document.metadata)
const { updateMetadata } = useMetadataManager()
const handleSave = async () => {
await updateMetadata(document.id, metadata)
onClose()
}
return (
<FocusView
title="Edit Metadata"
primaryAction=
Fetch Invoice Data:
export function useFetchACubeInvoiceAndCreditNotes(documentId: string) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
`${process.env.ACUBE_API_URL}/documents/${documentId}`,
{
headers: {
Status Synchronization:
export async function syncDocumentStatus(
stripePaymentId: string,
externalDocumentId: string,
newStatus: DocumentStatus
) {
// Update Stripe metadata
await stripe.paymentIntents.update(stripePaymentId, {
metadata: {
document_status: newStatus,
last_sync: new Date().toISOString()
}
})
// Update external system
await fetch(`${process.env.ACUBE_API_URL}/documents/${externalDocumentId}`, {
Store Configuration:
import { configureStore, createSlice } from '@reduxjs/toolkit'
const documentsSlice = createSlice({
name: 'documents',
initialState: {
current: null,
loading: false,
validationErrors: []
},
reducers: {
setDocument(state, action) {
state.current = action.payload
},
setValidationErrors(state, action) {
state.validationErrors = action.payload
}
# 1. Install dependencies
bun install
# 2. Start Stripe Apps development server
stripe apps start
# 3. View in Stripe Dashboard
# Navigate to any payment in test mode# Upload to Stripe account
stripe apps upload -p <account-id>
# Publish to production
stripe apps publish// Extension manifest (stripe-app.json)
{
"id": "com.acube.einvoicing",
"version": "1.0.0",
"name": "E-Invoicing Manager",
"icon": "./icon.svg",
"permissions": [
{
"permission": "payment_intent_read"
},
{
"permission": "payment_intent_write"
},
{
"permission": "refund_write"
}
],
"ui_extension": {
"views": [
{
"viewport": "stripe.dashboard.payment.detail",
"component": "PaymentDocumentDetailsView"
}
]
}
Stripe E-Invoicing Extension demonstrates seamless integration with the Stripe ecosystem, providing essential invoice management capabilities directly within the Stripe Dashboard with robust external system synchronization.
License: Proprietary (A-Cube S.r.l.)