Definition der SkriptsprachePrivate Vorschau
Erfahren Sie mehr über die Syntax und Semantik der in Stripe Billing verwendeten Skriptsprache.
Die Skriptsprache von Stripe ist eine Teilmenge von TypeScript. Obwohl die Funktionen von TypeScript beibehalten werden, weist es einige wichtige Unterschiede auf, darunter einen restriktiveren Satz zulässiger Vorgänge. Die Skriptsprache kann dazu beitragen, potenzielle Probleme wie lange Laufzeiten und übermäßigen Speicherverbrauch zu vermeiden.
Syntax
Basistypen und Literale
Die Sprache unterstützt die folgenden primitiven Typen:
// Numeric values let num: number = 42; let pi: number = 3.14159; // Strings let greeting: string = "Hello, world!"; let multiline: string = `This is a multi-line string`; // Booleans let active: boolean = true; let completed: boolean = false; // Null let empty: null = null;
Variablen und Erklärungen
Deklarieren Sie Variablen mit let
oder const
:
// Variable declarations let count: number = 0; let name: string = "John"; // Constants (must be initialized) const PI: number = 3.14159; const API_KEY: string = "abcd1234";
Typanmerkungen sind optional, werden aber empfohlen:
// Type inferred as number let score = 100; // Explicitly typed let highScore: number = 0;
Funktionen und Lambdas
Definieren Sie Funktionen auf verschiedene Weise:
// Function declaration function add(a: number, b: number): number { return a + b; } // Arrow function (lambda) const multiply = (a: number, b: number): number => { return a * b; }; // Inline arrow function const double = (x: number): number => x * 2;
Objekte und Arrays
Definieren Sie Objekte und Arrays ähnlich wie TypeScript:
// Object literal let person = { name: "Alice", age: 30, address: { city: "Wonderland", zipCode: "12345" } }; // Array literal let numbers = [1, 2, 3, 4, 5]; let names: string[] = ["Alice", "Bob", "Charlie"]; // Array spread let moreNumbers = [...numbers, 6, 7, 8, 9, 10]; // Accessing properties let cityName = person.address.city; let firstNumber = numbers[0];
Ablaufsteuerung
Zu den Ablaufsteuerungsanweisungen gehören if-else-, while- und for-Schleifen:
// If-else statement if (score > 100) { console.log("High score!"); } else if (score > 50) { console.log("Good score!"); } else { console.log("Try again!"); } // While loop let i = 0; while (i < 5) { console.log(i); i++; } // For loop for (let j = 0; j < 5; j++) { console.log(j); }
Break-Anweisungen funktionieren wie erwartet:
// Break in a loop let i = 0; while (true) { if (i >= 5) { break; } i++; }
Operatoren
Nachfolgend finden Sie unterstützte Standardoperatoren:
// Arithmetic operators let sum = 5 + 3; let difference = 10 - 4; let product = 3 * 7; let quotient = 20 / 4; let remainder = 10 % 3; // Modulo // Comparison operators let equal = (5 == 5); let notEqual = (5 != 3); let greater = (10 > 5); let less = (5 < 10); let greaterOrEqual = (5 >= 5); let lessOrEqual = (4 <= 4); // Logical operators let and = (true && false); // false let or = (true || false); // true let not = !true; // false // Compound assignment let value = 5; value += 3; // value is now 8
Import und Export
Organisieren Sie Ihren Code in Modulen:
// Importing modules import { DiscountableItem } from '@stripe/scripts/discounts'; // Export a function export function calculateTotal(items: DiscountableItem[]): number { let sum = 0; for (let i = 0; i < items.length; i++) { sum += items[i].price; } return sum; } // Default export export default function main() { // Main function logic }
Wir unterstützen keine Bibliotheken von Drittanbietern.
Typsystem
Typanmerkungen
Geben Sie Typen nach einem Doppelpunkt an:
let count: number = 0; let name: string = "John"; let active: boolean = true; let items: string[] = ["apple", "banana"];
Schnittstellen und Objekttypen
Definieren Sie Objekttypen inline oder als Schnittstellen:
// Inline object type let person: { name: string; age: number } = { name: "Alice", age: 30 }; // Interface definition interface Product { id: string; name: string; price: number; inStock?: boolean; // Optional property } // Using the interface let laptop: Product = { id: "lt-001", name: "Laptop", price: 999.99, inStock: true };
Unionstypen
Unionstypen ermöglichen es einer Variablen, mehrere Typen zu haben:
// Union type let id: string | number; id = "abc123"; // Valid id = 123; // Also valid
Typerklärungen
Erstellen von Typ-Aliassen:
// Type alias type ID = string | number; // Using the type alias let userId: ID = "user123"; let productId: ID = 456; // Complex type declaration type ApiResponse = { status: number; data: { items: any[]; count: number; }; error?: string; };
Statische Analyse
Unsere Skriptsprache beinhaltet statische Analysen, um sicherzustellen, dass Skripte zuverlässig und effizient sind.
Analyse der Beendigung
Eine wichtige Funktion ist die Beendigungsprüfung, die garantiert, dass Skripte immer beendet werden, wenn Sie sie auf der Infrastruktur von Stripe ausführen, wodurch Endlosschleifen oder Rekursion verhindert werden. Da nicht nachgewiesen werden kann, dass jeder Terminierungscode zur Beendigung führt, lehnen wir einige gültige Programme ab. Im Folgenden finden Sie Tipps zum Schreiben von Skripten zum Beenden.
Der Analyzer verwendet ein Färbesystem:
- T (Termination – Beendigung): Code, der nachweislich beendet wird.
- U (Unbekannt): Code, der möglicherweise nicht beendet wird.
// Guaranteed to terminate - marked as T function countdown(n: number): void { while (n > 0) { n = n - 1; // Decreasing counter } } // Not guaranteed to terminate - marked as U function infinite(): void { while (true) { console.log("This runs forever"); } }
Fundierte Rekursion
Der Analyzer prüft, ob rekursive Funktionen ein abnehmendes Maß haben:
// Safe recursion - marked as T function factorial(n: number): number { if (n <= 1) return 1; return n * factorial(n - 1); // n decreases with each call } // Unsafe recursion - marked as U function badRecursion(n: number): number { return badRecursion(n + 1); // n increases, no termination }
Häufige statische Analysemuster
For-Schleifen mit endlichen Grenzen sind sicher:
// Safe loop pattern - marked as T for (let i = 0; i < array.length; i++) { // Loop body with terminating operations }
While-Schleifen erfordern einen abnehmenden Zähler:
// Safe while loop - marked as T let counter = 10; while (counter > 0) { // Do something counter--; // Counter decreases }
Schreiben von beendigungssicherem Code
Befolgen Sie diese Richtlinien, um sicherzustellen, dass Ihr Code die Beendigungsprüfung besteht:
- Für Schleifen mit klaren, endlichen Grenzen verwenden
- Stellen Sie sicher, dass while-Schleifen einen abnehmenden Zähler oder eine abnehmende Bedingung haben, die schließlich
false
wird - Aufnahme eines Basisfalls und eines abnehmenden Arguments in rekursive Funktionen
- Vermeiden Sie komplexe Schleifenbedingungen, wechselseitige Rekursion oder tiefe Verschachtelung von Funktionen und Schleifen
// Good pattern for loops function processItems(items: any[]): void { for (let i = 0; i < items.length; i++) { processItem(items[i]); } }
Laufzeitumgebung
Die Skriptsprache stellt integrierte Objekte bereit, die Sie in Ihren Skripten verwenden können.
Integrierte Objekte
Es stehen mehrere integrierte Objekte zur Verfügung:
Mathematisches Objekt
// Math operations let min = Math.min(5, 3, 7); // 3 let max = Math.max(5, 3, 7); // 7 let floor = Math.floor(3.7); // 3 let ceil = Math.ceil(3.2); // 4
Arrays
// Array creation let numbers = [1, 2, 3, 4, 5]; // Array properties let length = numbers.length; // 5 // Array indexing let firstItem = numbers[0]; // 1 let lastItem = numbers[numbers.length - 1]; // 5 // Array methods let sorted = numbers.sort((a, b) => a - b);
Zeichenfolgenvorgänge
// String concatenation let firstName = "John"; let lastName = "Doe"; let fullName = firstName + " " + lastName; // "John Doe" // String with template literals let greeting = `Hello, ${firstName}!`; // "Hello, John!"
Beispiele
Prozentuale Rabattfunktion
Dieses Beispiel zeigt eine Rabattfunktion, die einen prozentualen Rabatt bis zu einem Höchstbetrag gewährt:
/** * Max Amount Percent Off Discount * * This discount function applies a percentage discount to the gross amount, * but caps the total discount at a maximum amount. It calculates the discount * as a percentage of the gross amount and then ensures it doesn't exceed the * configured maximum. * * Configuration: * - max_amount: The maximum monetary amount that can be discounted * - percent: The percentage discount to apply */ import { MonetaryAmount, Percent } from '@stripe/scripts'; import { DiscountFunction, DiscountableItem, DiscountResult, } from '@stripe/scripts/discounts'; type Configuration = { max_amount: MonetaryAmount; percent: Percent; }; const maxAmountPercentOff: DiscountFunction<Configuration> = ( configuration: Configuration, item: DiscountableItem, ): DiscountResult => { const { max_amount, percent } = configuration; const discount_amount = Math.min(max_amount.amount, item.gross_amount.amount * (percent / 100)); return { discount: { amount: { amount: discount_amount, currency: item.gross_amount.currency, }, status: discount_amount > 0 ? 'APPLIED' : 'NOT_APPLIED', reason: discount_amount > 0 ? 'Discount applied' : 'No discount applied', }, line_item_discounts: [], }; }; export default maxAmountPercentOff;
Gestaffelter Rabatt je nach Menge
Dieses Beispiel zeigt einen Rabatt, für den je nach Menge unterschiedliche Tarife gelten:
/** * Tiered Discount * * This discount function applies percentage discounts based on quantity tiers. * It sums the quantities across all line items and applies the discount percentage * from the highest applicable tier. The discount is calculated on each line item's * subtotal amount. * * Configuration: * - tiers: Array of objects with minimum_quantity and discount_percent * The tiers are sorted by minimum_quantity in descending order to find * the highest applicable tier. */ import { DiscountFunction, DiscountableItem, DiscountResult, ItemDiscount, } from '@stripe/scripts/discounts'; type Configuration = { tiers: Array<{ minimum_quantity: number; discount_percent: number; }>; }; const tieredDiscount: DiscountFunction<Configuration> = ( configuration: Configuration, item: DiscountableItem, ): DiscountResult => { let discountPercent = 0; let totalQuantity = 0; for (let i = 0; i < item.line_items.length; i++) { totalQuantity += item.line_items[i]?.quantity ?? 0; } const sortedTiers = [...configuration.tiers].sort( (a, b) => b.minimum_quantity - a.minimum_quantity, ); for (let i = 0; i < sortedTiers.length; i++) { const tier = sortedTiers[i]; if (totalQuantity >= tier.minimum_quantity) { discountPercent = tier.discount_percent; break; // Stop after finding the first applicable tier } } let totalDiscountAmount = 0; const lineItemDiscounts: ItemDiscount[] = []; for (let i = 0; i < item.line_items.length; i++) { const lineItem = item.line_items[i]; const lineItemDiscount = lineItem.subtotal.amount * (discountPercent / 100); totalDiscountAmount += lineItemDiscount; lineItemDiscounts.push({ discountable_item_id: lineItem.id, discount: { amount: { amount: lineItemDiscount, currency: lineItem.subtotal.currency, }, status: lineItemDiscount > 0 ? 'APPLIED' : 'NOT_APPLIED', reason: lineItemDiscount > 0 ? `${discountPercent}% tier discount applied` : 'No discount applied', }, }); } return { discount: { amount: { amount: totalDiscountAmount, currency: item.gross_amount.currency, }, status: totalDiscountAmount > 0 ? 'APPLIED' : 'NOT_APPLIED', reason: totalDiscountAmount > 0 ? `${discountPercent}% tier discount applied` : 'No discount applied', }, line_item_discounts: lineItemDiscounts, }; }; export default tieredDiscount;
Tipps zur Fehlerbehebung
Häufige Fehlermuster
Wenn die statische Analyse bei Ihrem Skript fehlschlägt, überprüfen Sie die folgenden häufigen Probleme:
Endlosschleifen: Schleifen ohne klare Beendigungsbedingung
// Problem: No clear exit condition while (x > 0) { doSomething(); // x never changes } // Fix: Add a decreasing counter while (x > 0) { doSomething(); x--; }
Nicht beendende Rekursion: Rekursive Aufrufe ohne abnehmendes Maß
// Problem: No decreasing measure function process(data: any): void { process(transformData(data)); } // Fix: Add a depth limit and decreasing measure function process(data: any, depth: number = 10): void { if (depth <= 0) return; process(transformData(data), depth - 1); }