App Review Quality Requirements
Stripe reviews every app submitted to the marketplace against a comprehensive set of quality requirements. Understanding these standards before development saves time and reduces the number of review iterations.
Overview
The Stripe App review evaluates your app across six key areas:
- Transparent Pricing — Clear communication of costs
- App Functionality — Reliability and completeness
- Developer Standards — Code quality and API usage
- UX Quality — User interface and experience standards
- Security — Data protection and secure practices
- Legal Compliance — Privacy and regulatory requirements
Transparent Pricing
Your app must clearly communicate all costs to users:
- Pricing disclosure: All pricing must be stated upfront in the marketplace listing
- No hidden fees: Users must not encounter unexpected charges after installation
- Trial terms: If offering a trial, clearly state the duration and what happens after it ends
- Upgrade flows: Any upsell or upgrade prompts must be non-intrusive and clearly optional
- Currency: Display prices in the user’s local currency when possible
Caution
Apps that obscure pricing or charge users without clear consent will be rejected immediately.
Date and Time Formatting
All dates and times displayed in your app must follow Stripe Dashboard conventions:
- Use the user’s locale for date formatting when available
- Display times in the user’s local timezone
- Use relative timestamps for recent events (e.g., “2 hours ago”)
- Use absolute timestamps for older events with full date and time
- Follow ISO 8601 for any API-facing date fields
// Good: Use Stripe's date formatting utilitiesimport { formatDate, formatRelativeTime } from '@stripe/ui-extension-sdk/utils';
const formattedDate = formatDate(timestamp); // Locale-awareconst relativeTime = formatRelativeTime(timestamp); // "2 hours ago"App Settings
If your app requires configuration:
- Provide a dedicated Settings view accessible from the app’s viewport
- Pre-populate sensible defaults where possible
- Validate all user inputs with clear error messages
- Allow users to update settings without reinstalling the app
- Persist settings across sessions using the Stripe Secret Store API
Sandbox Support
Your app must work correctly in Stripe’s sandbox (test) mode:
- Test mode compatibility: All features must function in test mode
- Test data: Use realistic test data that demonstrates app functionality
- No live data in sandbox: Never expose production data in test mode
- Graceful handling: If a feature is unavailable in sandbox, display a clear message explaining why
- Set
sandbox_install_compatible: truein your manifest
{ "sandbox_install_compatible": true}App Functionality
Reliability
- The app must not crash or freeze during normal use
- All advertised features must work as described
- Network errors must be handled gracefully with retry options
- The app must remain responsive during background operations
Completeness
- No placeholder content, “coming soon” features, or broken links
- All UI elements must be functional — no dead buttons or inactive controls
- Help text and documentation links must resolve to valid pages
- Uninstallation must cleanly remove all app data and webhooks
Performance
- UI must render within 3 seconds on a standard connection
- Background sync operations must not block the UI
- Large data sets must use pagination or lazy loading
- Minimize API calls to avoid rate limiting
Developer Standards
API Usage
- Use the latest stable version of the Stripe API
- Follow Stripe API best practices for pagination, error handling, and idempotency
- Do not exceed rate limits — implement exponential backoff for retries
- Use webhooks for event-driven updates instead of polling
Code Quality
- No console errors or warnings in production builds
- Remove all debug logging before submission
- Handle all edge cases (empty states, missing data, network failures)
- Follow Stripe’s component library patterns for consistent UI
Versioning
- Use semantic versioning (MAJOR.MINOR.PATCH)
- Document breaking changes in version updates
- Maintain backward compatibility where possible
UX Quality
Advertising
- No ads: Your app must not display advertisements of any kind
- No cross-promotion: Do not promote other products or services within the app UI
- Branded content: Only display your own brand identity, not third-party brands (except integration partners like Brevo)
Language and Content
- Consistent language: Use consistent terminology throughout the app
- Professional tone: Match the Stripe Dashboard’s professional, concise communication style
- No jargon: Avoid technical jargon that merchants may not understand
- Spell check: Ensure all text is free of spelling and grammatical errors
- English only: All user-facing text must be in English for marketplace apps
Confirmation Dialogs
Require user confirmation for destructive or significant actions:
// Good: Confirm before destructive actionsconst handleDisconnect = async () => { const confirmed = await showConfirmation({ title: 'Disconnect Brevo Integration', message: 'This will stop syncing customer data to Brevo. You can reconnect at any time.', confirmLabel: 'Disconnect', cancelLabel: 'Cancel', destructive: true, });
if (confirmed) { await disconnectIntegration(); }};Actions that require confirmation:
- Disconnecting integrations
- Deleting synced data
- Changing settings that affect data flow
- Resetting configuration to defaults
Loading States
Always show loading indicators for asynchronous operations:
// Good: Show loading state during data fetchconst CustomerSyncStatus = () => { const { data, isLoading, error } = useSyncStatus();
if (isLoading) { return <Spinner label="Loading sync status..." />; }
if (error) { return <Banner type="critical" title="Failed to load sync status"> {error.message} </Banner>; }
return <SyncStatusDisplay data={data} />;};Requirements:
- Show spinners or skeleton screens during data loading
- Disable buttons during form submission
- Display progress indicators for long-running operations
- Never show a blank screen while loading
Error Messages
Provide clear, actionable error messages:
// Bad: Generic error"Something went wrong"
// Good: Specific and actionable"Unable to sync customer data to Brevo. Please verify your Brevo API keyin Settings and try again."Error message guidelines:
- Explain what happened in plain language
- Suggest a specific action the user can take to resolve the issue
- Provide a way to retry the failed operation
- Log detailed error information for debugging (not shown to users)
- Include error codes for support reference when applicable
Security
Secret Store API
Use Stripe’s Secret Store API for all sensitive data storage:
import { createHttpClient, STRIPE_API_KEY } from '@stripe/ui-extension-sdk/http_client';
// Good: Store secrets using the Secret Store APIconst storeBrevoApiKey = async (apiKey: string) => { const stripe = createHttpClient(STRIPE_API_KEY); await stripe.apps.secrets.create({ name: 'brevo_api_key', payload: apiKey, scope: { type: 'account' }, });};
// Good: Retrieve secrets from the Secret Storeconst getBrevoApiKey = async () => { const stripe = createHttpClient(STRIPE_API_KEY); const secret = await stripe.apps.secrets.find({ name: 'brevo_api_key', scope: { type: 'account' }, }); return secret.payload;};Never store sensitive data in:
- Local storage or session storage
- Cookies
- URL parameters
- Hardcoded values in source code
- Plain text configuration files
Cryptography
- No custom cryptography: Do not implement your own encryption algorithms
- Use Stripe’s built-in security primitives (Secret Store, signing secrets)
- Use HTTPS for all external API calls
- Validate all webhook signatures before processing
Data Handling
- Only request permissions your app actually needs
- Do not store Stripe data outside of what is necessary for functionality
- Implement data retention policies aligned with your privacy policy
- Provide a mechanism for users to request data deletion
Legal Compliance
Privacy Policy
Your app must have a publicly accessible privacy policy that covers:
- What data your app collects from Stripe
- How the data is stored, processed, and shared
- Data retention and deletion policies
- User rights regarding their data
- Contact information for privacy inquiries
- Compliance with applicable regulations (GDPR, CCPA, etc.)
Terms of Service
- Provide clear terms of service for your app
- Do not include terms that conflict with Stripe’s Terms of Service
- Clearly state any usage limitations or restrictions
Regulatory Compliance
- Comply with all applicable data protection regulations
- Implement appropriate data processing agreements
- Support data portability and deletion requests
- Maintain audit logs for data access and processing
Review Process Timeline
| Stage | Duration |
|---|---|
| Initial submission | 5-10 business days |
| Revision review | 3-7 business days |
| Final approval | 1-2 business days |
| Publication | Immediate after approval |
Tip
Address all review feedback in a single revision to avoid multiple review cycles. The Stripe team provides specific, actionable feedback for each issue found.
Common Rejection Reasons
- Missing error handling — App crashes on network errors or unexpected data
- Insufficient loading states — Blank screens during data fetching
- Unclear pricing — Pricing not fully disclosed in the listing
- Excessive permissions — Requesting permissions not needed by the app
- Broken sandbox mode — App doesn’t function in test mode
- Security issues — Storing secrets outside the Secret Store API
- Missing privacy policy — No accessible privacy policy URL
- Incomplete functionality — “Coming soon” features or placeholder content