תרחיש טיפוסי: מוצר SaaS B2B שגדל ב-20% מדי חודש, אבל ההתחשבנות עדיין ידנית. מנהלים מבזבזים עד 15 דקות על כל חשבונית—העתקת נתונים, הדבקה ב-Word, אובדן סכומים, טעויות במספרי עוסק מורשה. לקוחות מתלוננים על עיכובים של 2-3 ימים, וחשבונאות מבזבזת שעות על התאמות. אוטומציה של חשבוניות מצמצמת תהליך זה ל-30 שניות—פי 30 מהר יותר, וחוסכת עד 10,000 דולר בשנה לחברה עם 500 חשבוניות חודשיות. אנו מיישמים את המחזור המלא: מהגדרת Stripe ועד יצירת PDF מותאם אישית עם אחסון S3 וסנכרון 1C.
בעסק SaaS עם מאות לקוחות ומנויים במטבעות שונים, התחשבנות ידנית הופכת לצוואר בקבוק. כל שגיאה בפרטים או במסים עלולה לעלות אלפי דולרים. ראינו פרויקטים שבהם ל-15% מהחשבוניות היו שגיאות—לאחר אוטומציה, נתון זה ירד ל-0.5%. "אוטומציה של חשבוניות חסכה לנו 40 שעות בחודש והפחיתה תשלומים מאוחרים ב-30%"—סמנכ"ל כספים של לקוח. הגדרה נכונה של Stripe Invoicing ויצירת PDF מותאם אישית לא רק מאיצה את התהליך אלא גם בונה אמון לקוחות: חשבוניות נראות מקצועיות ומכילות נתונים נכונים.
הצוות שלנו יישם התחשבנות ל-30+ פרויקטי SaaS, מסטארטאפים ועד ארגונים גדולים. אנו מכירים את כל המלכודות: ממספרי עוסק מורשה ועד אזורי זמן. אם אתה מתמודד עם נפח חשבוניות גדל, קבל ייעוץ עם מהנדס.
מדוע אוטומציה של חשבוניות קריטית ל-SaaS
עם כל לקוח חדש, עומס החשבונאות גדל. שגיאות בפרטים, מסים ומטבעות מובילות לעיכובים בתשלומים ולאובדן אמון. התחשבנות אוטומטית מבטלת שגיאות אנוש ביצירת חשבוניות, מאיצה את ההנפקה לשניות לאחר תשלום המנוי, תומכת במספר מטבעות (USD, EUR, RUB) ובמשטרי מס (מע"מ, מס מכירות בארה"ב), ומייצרת חשבוניות PDF מותאמות מיתוג לכל לקוח. אינטגרציה עם הנהלת חשבונות מסנכרנת נתונים אוטומטית עם 1C ומערכות אחרות.
בעיות שאנו פותרים
- עמימות מס — Stripe תומך במספרי עוסק מורשה (INN לרוסיה, VAT לאיחוד האירופי) אך דורש הגדרה נכונה. אנו מגדירים קביעה אוטומטית של שיעור מס לפי מדינת הלקוח.
- מיתוג מותאם אישית — חשבוניות Stripe סטנדרטיות rarely מספקות לקוחות. אנו יוצרים תבניות PDF מותאמות אישית באמצעות @react-pdf/renderer, עם שליטה מלאה בעיצוב ובמיקום השדות.
- אחסון ומשלוח — חשבוניות PDF חייבות להיות נגישות מפורטל הלקוח ולהישלח בדוא"ל. אנו משתמשים ב-S3 עם כתובות URL חתומות מראש וב-SendGrid למשלוח.
- סנכרון הנהלת חשבונות — דרך Stripe webhook או יצירה מותאמת אישית, אנו מעבירים נתונים ל-1C ולמערכות אחרות.
כיצד להבטיח דיוק מס בחשבוניות
חוקי המס משתנים לפי מדינה ומדינה בארה"ב. Stripe מאפשר להגדיר שיעורי מס גלובליים, אך לתרחישים מורכבים (למשל, מע"מ תוך-איחוד אירופי), נדרשת לוגיקה נוספת. אנו משתמשים ב-Stripe Tax API לחישוב מסים אוטומטי לפי כתובת הלקוח וסוג המוצר. עבור PDF מותאם אישית, מסים מחושבים בצד השרת באמצעות ספריות מהימנות. בדיקות על תרחישים אמיתיים (Stripe sandbox) מבטיחות שכל עסקה עוברת ללא חסימות.
כיצד יישמנו התחשבנות לסטארטאפ SaaS
לקוח: SaaS אמריקאי לניהול פרילנסרים. דרישות: הנפקת חשבוניות ב-USD ו-EUR, עם לוגו הלקוח, תנאי תשלום 15/30 יום, ושמירה אוטומטית של PDF ל-S3. בחרנו בגישה היברידית: מנויים דרך Stripe Invoicing, יצירת PDF דרך React PDF עם רכיב מותאם אישית.
דוגמה להגדרת לקוח ב-Stripe
// Настройка Customer с реквизитами для инвойсов
const customer = await stripe.customers.create({
email: '[email protected]',
name: 'Acme Corp',
address: {
line1: 'ул. Ленина, 1',
city: 'Москва',
country: 'RU',
postal_code: '101000',
},
tax_ids: [
{ type: 'ru_inn', value: '7727563778' },
],
metadata: { tenantId },
});
// Кастомный брендинг через Stripe Dashboard:
// Settings → Branding → Logo, colors, footer text
// Обновление реквизитов клиентом
export async function updateBillingDetails(
tenantId: string,
data: BillingDetailsInput
): Promise<void> {
const subscription = await db.subscription.findUnique({
where: { tenantId }
});
await stripe.customers.update(subscription!.stripeCustomerId, {
name: data.companyName,
email: data.billingEmail,
address: {
line1: data.address,
city: data.city,
country: data.country,
postal_code: data.postalCode,
},
});
// Tax ID (ИНН для RU, VAT для EU)
if (data.taxId) {
// Сначала удаляем старые tax IDs
const existingCustomer = await stripe.customers.retrieve(
subscription!.stripeCustomerId,
{ expand: ['tax_ids'] }
) as Stripe.Customer;
for (const taxId of (existingCustomer.tax_ids as Stripe.ApiList<Stripe.TaxId>).data) {
await stripe.customers.deleteTaxId(subscription!.stripeCustomerId, taxId.id);
}
// Добавляем новый
await stripe.customers.createTaxId(subscription!.stripeCustomerId, {
type: data.taxIdType as Stripe.TaxIdCreateParams.Type,
value: data.taxId,
});
}
}יצירת PDF דרך React PDF עם מטמון S3: הבקשה הראשונה יוצרת ושומרת, ובקשות עוקבות מגישות את הקובץ השמור. זה מפחית עומס ומאיץ את המשלוח.
// npm install @react-pdf/renderer
import { pdf } from '@react-pdf/renderer';
import { InvoicePDF } from '@/components/pdf/InvoicePDF';
export async function generateInvoicePDF(invoiceId: string): Promise<Buffer> {
const invoice = await db.invoice.findUnique({
where: { id: invoiceId },
include: {
tenant: {
include: { branding: true }
},
lineItems: true,
}
});
const pdfStream = await pdf(
<InvoicePDF invoice={invoice!} />
).toBuffer();
return pdfStream;
}
// Сохраняем в S3 и возвращаем URL
export async function getInvoicePdfUrl(invoiceId: string): Promise<string> {
const key = `invoices/${invoiceId}.pdf`;
// Проверяем, уже создан ли
try {
await s3.headObject({ Bucket: process.env.AWS_BUCKET!, Key: key }).promise();
return `https://${process.env.AWS_BUCKET}.s3.amazonaws.com/${key}`;
} catch {
// Не найден — генерируем
}
const pdfBuffer = await generateInvoicePDF(invoiceId);
await s3.putObject({
Bucket: process.env.AWS_BUCKET!,
Key: key,
Body: pdfBuffer,
ContentType: 'application/pdf',
ContentDisposition: `attachment; filename="invoice-${invoiceId}.pdf"`,
}).promise();
return `https://${process.env.AWS_BUCKET}.s3.amazonaws.com/${key}`;
}רכיב החשבונית מפורט: טבלה עם תיאור, כמות, מחיר, סה"כ; בלוקים של "מאת" ו-"אל"; סטטוס תשלום. כל הנתונים מגיעים ממסד הנתונים, מה שמבטיח דיוק.
// components/pdf/InvoicePDF.tsx
import { Document, Page, Text, View, Image, StyleSheet } from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: {
padding: 40,
fontSize: 11,
fontFamily: 'Helvetica'
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 40
},
title: {
fontSize: 24,
fontWeight: 'bold'
},
table: {
marginTop: 20
},
tableRow: {
flexDirection: 'row',
borderBottom: '1px solid #eee',
padding: '8px 0'
},
tableHeader: {
backgroundColor: '#f5f5f5',
fontWeight: 'bold'
},
});
export function InvoicePDF({ invoice }: { invoice: Invoice }) {
return (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.header}>
<View>
{invoice.tenant.branding?.logoUrl && (
<Image src={invoice.tenant.branding.logoUrl} style={{ height: 40 }} />
)}
<Text style={styles.title}>СЧЁТ-ФАКТУРА</Text>
<Text>№ {invoice.number}</Text>
<Text>от {invoice.createdAt.toLocaleDateString('ru-RU')}</Text>
</View>
<View style={{ alignItems: 'flex-end' }}>
<Text style={{ fontSize: 18, color: '#6366f1' }}>
{formatCurrency(invoice.total, invoice.currency)}
</Text>
<Text style={{ color: invoice.status === 'paid' ? '#22c55e' : '#f59e0b' }}>
{invoice.status === 'paid' ? 'Оплачен' : 'Ожидает оплаты'}
</Text>
</View>
</View>
{/* Детали компании */}
<View style={{ flexDirection: 'row', gap: 40, marginBottom: 30 }}>
<View>
<Text style={{ fontWeight: 'bold', marginBottom: 4 }}>От:</Text>
<Text>{process.env.COMPANY_NAME}</Text>
<Text>ИНН: {process.env.COMPANY_INN}</Text>
</View>
<View>
<Text style={{ fontWeight: 'bold', marginBottom: 4 }}>Кому:</Text>
<Text>{invoice.customerName}</Text>
{invoice.taxId && <Text>ИНН: {invoice.taxId}</Text>}
</View>
</View>
{/* Строки инвойса */}
<View style={styles.table}>
<View style={[styles.tableRow, styles.tableHeader]}>
<Text style={{ flex: 3 }}>Описание</Text>
<Text style={{ flex: 1, textAlign: 'right' }}>Кол-во</Text>
<Text style={{ flex: 1, textAlign: 'right' }}>Цена</Text>
<Text style={{ flex: 1, textAlign: 'right' }}>Сумма</Text>
</View>
{invoice.lineItems.map((item) => (
<View key={item.id} style={styles.tableRow}>
<Text style={{ flex: 3 }}>{item.description}</Text>
<Text style={{ flex: 1, textAlign: 'right' }}>{item.quantity}</Text>
<Text style={{ flex: 1, textAlign: 'right' }}>
{formatCurrency(item.unitAmount, invoice.currency)}
</Text>
<Text style={{ flex: 1, textAlign: 'right' }}>
{formatCurrency(item.amount, invoice.currency)}
</Text>
</View>
))}
</View>
{/* Итого */}
<View style={{ alignItems: 'flex-end', marginTop: 20 }}>
<Text style={{ fontSize: 14, fontWeight: 'bold' }}>
Итого: {formatCurrency(invoice.total, invoice.currency)}
</Text>
</View>
</Page>
</Document>
);
} כיצד פועל סנכרון Webhook
ה-webhook של // Настройка Customer с реквизитами для инвойсов const customer = await stripe.customers.create({ email: '[email protected]', name: 'Acme Corp', address: { line1: 'ул. Ленина, 1', city: 'Москва', country: 'RU', postal_code: '101000', }, tax_ids: [{ type: 'ru_inn', value: '7727563778', }], metadata: { tenantId }, }); // Кастомный брендинг через Stripe Dashboard: // Settings → Branding → Logo, colors, footer text שולח נתונים על חשבונית חדשה. אנו מבצעים upsert של הרשומה במסד הנתונים שלנו ושומרים את כתובת ה-PDF מ-Stripe. עבור חשבוניות מותאמות אישית, אנו יוצרים את ה-PDF ומעלים ל-S3. זה נותן ללקוח נראות בזמן אמת של חשבוניות.
case 'invoice.finalized': {
const stripeInvoice = event.data.object as Stripe.Invoice;
await db.invoice.upsert({
where: { stripeInvoiceId: stripeInvoice.id },
create: {
stripeInvoiceId: stripeInvoice.id,
tenantId: stripeInvoice.metadata.tenantId,
number: stripeInvoice.number!,
total: stripeInvoice.amount_due,
currency: stripeInvoice.currency,
status: 'open',
pdfUrl: stripeInvoice.invoice_pdf,
periodStart: new Date(stripeInvoice.period_start * 1000),
periodEnd: new Date(stripeInvoice.period_end * 1000),
},
update: { status: 'open' }
});
break;
} מה כלול
| שלב | פרטים |
|---|---|
| ניתוח | ביקורת התהליך הנוכחי, הסכמה על פורמט החשבונית, דרישות מס, מטבעות |
| עיצוב | ארכיטקטורה: בחירה בין Stripe Invoicing ליצירה מותאמת אישית, סכמת webhook, מודל נתונים |
| יישום | הגדרת Stripe, פיתוח תבנית PDF, מטפלי webhook, אינטגרציה עם S3 ודוא"ל |
| בדיקות | אימות כל התרחישים: יצירה, עדכון, ביטול, החזר, מטבעות שונים |
| תיעוד | מפרט API, הוראות חשבונאות, מדריך מיתוג |
| פריסה | פריסה לייצור, ניטור שגיאות, הגדרת התראות |
| תמיכה | אחריות לחודש (תיקוני באגים בחינם), הדרכת צוות אופציונלית |
בעיות נפוצות ופתרונות
| בעיה | השלכה | פתרון |
|---|---|---|
| מספר עוסק מורשה שגוי | מס לא מחושב | אימות פורמט דרך Stripe validation |
| אין מטמון PDF | עומס גבוה | אחסון ב-S3 עם כתובות URL חתומות מראש |
| התעלמות מ-idempotency | חשבוניות כפולות | שימוש ב-idempotency_key |
| אי-התחשבות באזורי זמן | אי-התאמות בתאריכים | הגדרת אזור זמן של הלקוח |
לוחות זמנים משוערים
- הגדרת Stripe Invoicing בסיסית — 3 עד 5 ימים (ללא PDF מותאם אישית).
- יצירת PDF מותאם אישית עם S3 ו-webhooks — 7 עד 10 ימים.
- מחזור מלא עם אינטגרציית 1C — 10 עד 14 ימים.
העלות מחושבת באופן אישי לאחר ביקורת—צור קשר לייעוץ.
הניסיון שלנו: 10+ שנים בייצור, 40+ פרויקטים. אנו מבטיחים הגדרה נכונה ותמיכה. צור קשר כדי לדון בפרויקט שלך ולקבל ייעוץ.







