Skip to content

External Services Setup

This guide covers the setup and configuration of all external/3rd party services required for the Iran Nation project, including Firebase, Google Services, iOS integrations, monitoring, email services, and cloud storage.

ServicePurposeUsed ByEnvironment
FirebaseAuthentication, Push Notifications, AnalyticsMobile, BackendAll
Google ServicesOAuth, Maps, Cloud APIsMobile, BackendAll
Apple DeveloperiOS App Store, Push NotificationsMobile (iOS)Production
Expo ServicesEAS Build, Updates, NotificationsMobileAll
SentryError Tracking & PerformanceMobile, Backend, WebAll
ResendTransactional EmailBackendAll
AWS S3File Storage & CDNBackendAll
StripePayment ProcessingMobile, BackendProduction

Firebase provides authentication, push notifications, analytics, and real-time database services.

Terminal window
# Install Firebase CLI
npm install -g firebase-tools
# Login to Firebase
firebase login
# Create new project (or use existing)
firebase projects:create iran-nation-app
  1. Create iOS App in Firebase Console:

    • Bundle ID: com.irannetworkstate.app
    • Download GoogleService-Info.plist
    • Place in PersiaNation/ios/PersiaNation/
  2. Create Android App in Firebase Console:

    • Package name: com.irannetworkstate.app
    • Download google-services.json
    • Place in PersiaNation/android/app/
  3. Configure Expo App:

app.config.ts
export default {
plugins: [
[
"@react-native-firebase/app",
{
// Firebase configuration will be auto-detected
},
],
[
"@react-native-firebase/messaging",
{
// Enable push notifications
ios: {
requestPermission: true,
},
},
],
],
ios: {
googleServicesFile: "./GoogleService-Info.plist",
},
android: {
googleServicesFile: "./google-services.json",
},
};
Terminal window
# Install Firebase Admin SDK
npm install firebase-admin
# Generate service account key from Firebase Console
# Download JSON file and save as firebase-credentials.json
src/firebase/firebase.service.ts
import { Injectable } from "@nestjs/common";
import * as admin from "firebase-admin";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class FirebaseService {
private app: admin.app.App;
constructor(private configService: ConfigService) {
const serviceAccount = require("../../firebase-credentials.json");
this.app = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
projectId: this.configService.get("FIREBASE_PROJECT_ID"),
});
}
async sendNotification(token: string, payload: any) {
return await admin.messaging().send({
token,
notification: payload.notification,
data: payload.data,
});
}
async verifyIdToken(idToken: string) {
return await admin.auth().verifyIdToken(idToken);
}
}
Terminal window
# .env files
FIREBASE_PROJECT_ID=iran-nation-app
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxx@iran-nation-app.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
// Firestore Security Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Users can only access their own data
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Public read for announcements
match /announcements/{document} {
allow read: if true;
allow write: if request.auth != null &&
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin';
}
}
}
  1. Create Google Cloud Project:

    • Go to Google Cloud Console
    • Create new project: iran-nation-services
    • Enable billing for the project
  2. Enable Required APIs:

Terminal window
# Enable APIs via CLI
gcloud services enable gmail.googleapis.com
gcloud services enable drive.googleapis.com
gcloud services enable maps-backend.googleapis.com
gcloud services enable places-backend.googleapis.com
gcloud services enable geocoding-backend.googleapis.com
Terminal window
# Create service account
gcloud iam service-accounts create iran-nation-backend \
--description="Backend service account" \
--display-name="Iran Nation Backend"
# Create and download key
gcloud iam service-accounts keys create ./google-service-account.json \
--iam-account=iran-nation-backend@iran-nation-services.iam.gserviceaccount.com
  1. Create OAuth Client ID:

    • Application type: iOS/Android
    • Bundle ID: com.irannetworkstate.app
    • Download configuration files
  2. Configure Expo:

app.config.ts
export default {
plugins: [
[
"@react-native-google-signin/google-signin",
{
webClientId: "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
},
],
],
};
// Web OAuth configuration
const googleAuthConfig = {
clientId: "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com",
redirectUri: "https://yourapp.com/auth/google/callback",
scopes: ["openid", "profile", "email"],
};
app.config.ts
// Mobile app configuration
export default {
plugins: [
[
"react-native-maps",
{
googleMapsApiKey: process.env.GOOGLE_MAPS_API_KEY,
},
],
],
};
src/maps/maps.service.ts
// Backend configuration
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class MapsService {
constructor(private configService: ConfigService) {}
async geocode(address: string) {
const apiKey = this.configService.get("GOOGLE_MAPS_API_KEY");
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(
address
)}&key=${apiKey}`
);
return response.json();
}
}
  1. Enroll in Apple Developer Program: $99/year
  2. Create App Identifier:
    • Bundle ID: com.irannetworkstate.app
    • Enable capabilities: Push Notifications, Sign in with Apple
  1. Create APNs Key:

    • Go to Certificates, Identifiers & Profiles
    • Create new Key with APNs enabled
    • Download .p8 file
  2. Configure in Firebase:

    • Upload APNs key to Firebase Console
    • Set Key ID and Team ID
Terminal window
# Install App Store Connect API key
# Download API key from App Store Connect
# Configure in CI/CD pipeline
# EAS configuration
eas secret:create --scope project --name APPLE_API_KEY --value "$(cat AuthKey_XXXXXXXXXX.p8)"
eas secret:create --scope project --name APPLE_API_KEY_ID --value "XXXXXXXXXX"
eas secret:create --scope project --name APPLE_API_ISSUER_ID --value "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
// Mobile app configuration
import { AppleButton } from "@invertase/react-native-apple-authentication";
const signInWithApple = async () => {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: appleAuth.Operation.LOGIN,
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
});
// Send to backend for verification
const response = await api.post("/auth/apple", {
identityToken: appleAuthRequestResponse.identityToken,
authorizationCode: appleAuthRequestResponse.authorizationCode,
});
};
Terminal window
# Install EAS CLI
npm install -g @expo/cli eas-cli
# Login to Expo
expo login
# Configure project
eas init --id your-project-id
eas.json
{
"cli": {
"version": ">= 5.0.0",
"appVersionSource": "local"
},
"build": {
"production": {
"channel": "production",
"env": {
"EXPO_PUBLIC_API_URL": "https://api.irannetworkstate.com",
"EXPO_PUBLIC_ENVIRONMENT": "production"
}
},
"staging": {
"channel": "staging",
"env": {
"EXPO_PUBLIC_API_URL": "https://staging-api.irannetworkstate.com",
"EXPO_PUBLIC_ENVIRONMENT": "staging"
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "your-apple-id@email.com",
"ascAppId": "1234567890",
"appleTeamId": "XXXXXXXXXX"
},
"android": {
"serviceAccountKeyPath": "./google-play-service-account.json",
"track": "production"
}
}
}
}
src/notifications/expo-notifications.service.ts
import { Injectable } from "@nestjs/common";
import { Expo, ExpoPushMessage } from "expo-server-sdk";
@Injectable()
export class ExpoNotificationsService {
private expo = new Expo({
accessToken: process.env.EXPO_ACCESS_TOKEN,
});
async sendPushNotification(pushToken: string, message: string, data?: any) {
const messages: ExpoPushMessage[] = [
{
to: pushToken,
sound: "default",
body: message,
data,
},
];
const chunks = this.expo.chunkPushNotifications(messages);
const tickets = [];
for (const chunk of chunks) {
const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk);
tickets.push(...ticketChunk);
}
return tickets;
}
}

Sentry provides error tracking and performance monitoring across all platforms.

Terminal window
# Install Sentry CLI
npm install -g @sentry/cli
# Create Sentry account and project
# Get DSN from Sentry dashboard
Terminal window
# Install Sentry for React Native
cd PersiaNation
npx @sentry/react-native install
app.config.ts
export default {
plugins: [
[
"@sentry/react-native/expo",
{
organization: "iran-network-state",
project: "persia-nation-mobile",
authToken: process.env.SENTRY_AUTH_TOKEN,
},
],
],
};
src/lib/sentry.ts
import * as Sentry from "@sentry/react-native";
Sentry.init({
dsn: "https://your-dsn@sentry.io/project-id",
environment: process.env.EXPO_PUBLIC_ENVIRONMENT,
enableAutoSessionTracking: true,
sessionTrackingIntervalMillis: 10000,
});
export default Sentry;
Terminal window
# Install Sentry for NestJS
cd ins-nest
npm install @sentry/node @sentry/tracing
src/main.ts
import * as Sentry from "@sentry/node";
import "@sentry/tracing";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
});
// Add Sentry request handler
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());
// Add error handler before other error middleware
app.use(Sentry.Handlers.errorHandler());
web/src/lib/sentry.ts
import * as Sentry from "@sentry/astro";
Sentry.init({
dsn: "https://your-dsn@sentry.io/project-id",
environment: import.meta.env.MODE,
integrations: [new Sentry.BrowserTracing()],
tracesSampleRate: 1.0,
});
Terminal window
# Sign up at resend.com
# Get API key from dashboard
# Verify domain for production use
Terminal window
# Install Resend SDK
npm install resend
src/email/resend.service.ts
import { Injectable } from "@nestjs/common";
import { Resend } from "resend";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class ResendService {
private resend: Resend;
constructor(private configService: ConfigService) {
this.resend = new Resend(this.configService.get("RESEND_API_KEY"));
}
async sendWelcomeEmail(to: string, name: string) {
return await this.resend.emails.send({
from: "Iran Nation <noreply@irannetworkstate.com>",
to,
subject: "Welcome to Iran Nation",
html: `
<h1>Welcome ${name}!</h1>
<p>Thank you for joining Iran Nation.</p>
`,
});
}
async sendPasswordReset(to: string, resetToken: string) {
const resetUrl = `${this.configService.get(
"FRONTEND_URL"
)}/reset-password?token=${resetToken}`;
return await this.resend.emails.send({
from: "Iran Nation <noreply@irannetworkstate.com>",
to,
subject: "Reset Your Password",
html: `
<h1>Password Reset Request</h1>
<p>Click the link below to reset your password:</p>
<a href="${resetUrl}">Reset Password</a>
<p>This link expires in 1 hour.</p>
`,
});
}
}
src/email/templates/welcome.template.ts
export const welcomeEmailTemplate = (name: string, verificationUrl: string) => `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Welcome to Iran Nation</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { text-align: center; margin-bottom: 30px; }
.button {
display: inline-block;
padding: 12px 24px;
background-color: #5d4037;
color: white;
text-decoration: none;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Welcome to Iran Nation, ${name}!</h1>
</div>
<p>Thank you for joining our community. To get started, please verify your email address:</p>
<p style="text-align: center;">
<a href="${verificationUrl}" class="button">Verify Email Address</a>
</p>
<p>If you didn't create an account, you can safely ignore this email.</p>
<p>Best regards,<br>The Iran Nation Team</p>
</div>
</body>
</html>
`;
Terminal window
# Install AWS CLI
aws configure
# Enter Access Key ID, Secret Access Key, Region, Output format
# Create S3 bucket
aws s3 mb s3://iran-nation-storage --region us-east-1
# Set bucket policy for public read access (for public assets)
aws s3api put-bucket-policy --bucket iran-nation-storage --policy file://bucket-policy.json
bucket-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::iran-nation-storage/public/*"
}
]
}
Terminal window
# Install AWS SDK
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner multer-s3
src/file-storage/s3.service.ts
import { Injectable } from "@nestjs/common";
import {
S3Client,
PutObjectCommand,
DeleteObjectCommand,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class S3Service {
private s3Client: S3Client;
private bucketName: string;
constructor(private configService: ConfigService) {
this.s3Client = new S3Client({
region: this.configService.get("AWS_REGION"),
credentials: {
accessKeyId: this.configService.get("AWS_ACCESS_KEY_ID"),
secretAccessKey: this.configService.get("AWS_SECRET_ACCESS_KEY"),
},
});
this.bucketName = this.configService.get("AWS_S3_BUCKET");
}
async uploadFile(key: string, file: Buffer, contentType: string) {
const command = new PutObjectCommand({
Bucket: this.bucketName,
Key: key,
Body: file,
ContentType: contentType,
});
await this.s3Client.send(command);
return `https://${this.bucketName}.s3.amazonaws.com/${key}`;
}
async getSignedUploadUrl(key: string, contentType: string, expiresIn = 3600) {
const command = new PutObjectCommand({
Bucket: this.bucketName,
Key: key,
ContentType: contentType,
});
return await getSignedUrl(this.s3Client, command, { expiresIn });
}
async deleteFile(key: string) {
const command = new DeleteObjectCommand({
Bucket: this.bucketName,
Key: key,
});
await this.s3Client.send(command);
}
}
src/file-storage/file-storage.controller.ts
import {
Controller,
Post,
UseInterceptors,
UploadedFile,
Body,
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";
import { S3Service } from "./s3.service";
@Controller("files")
export class FileStorageController {
constructor(private s3Service: S3Service) {}
@Post("upload")
@UseInterceptors(FileInterceptor("file"))
async uploadFile(@UploadedFile() file: Express.Multer.File) {
const key = `uploads/${Date.now()}-${file.originalname}`;
const url = await this.s3Service.uploadFile(
key,
file.buffer,
file.mimetype
);
return { url, key };
}
@Post("signed-url")
async getSignedUploadUrl(
@Body() { filename, contentType }: { filename: string; contentType: string }
) {
const key = `uploads/${Date.now()}-${filename}`;
const signedUrl = await this.s3Service.getSignedUploadUrl(key, contentType);
return { signedUrl, key };
}
}
Terminal window
# Sign up at stripe.com
# Get publishable and secret keys from dashboard
# Configure webhooks endpoint
Terminal window
# Install Stripe SDK
npm install stripe
src/stripe/stripe.service.ts
import { Injectable } from "@nestjs/common";
import Stripe from "stripe";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class StripeService {
private stripe: Stripe;
constructor(private configService: ConfigService) {
this.stripe = new Stripe(this.configService.get("STRIPE_SECRET_KEY"), {
apiVersion: "2023-10-16",
});
}
async createPaymentIntent(
amount: number,
currency = "usd",
customerId?: string
) {
return await this.stripe.paymentIntents.create({
amount: amount * 100, // Convert to cents
currency,
customer: customerId,
automatic_payment_methods: {
enabled: true,
},
});
}
async createCustomer(email: string, name: string) {
return await this.stripe.customers.create({
email,
name,
});
}
async handleWebhook(payload: string, signature: string) {
const endpointSecret = this.configService.get("STRIPE_WEBHOOK_SECRET");
try {
const event = this.stripe.webhooks.constructEvent(
payload,
signature,
endpointSecret
);
switch (event.type) {
case "payment_intent.succeeded":
// Handle successful payment
break;
case "payment_intent.payment_failed":
// Handle failed payment
break;
default:
console.log(`Unhandled event type ${event.type}`);
}
return { received: true };
} catch (err) {
throw new Error(`Webhook signature verification failed: ${err.message}`);
}
}
}
Terminal window
# Install Stripe React Native SDK
cd PersiaNation
npm install @stripe/stripe-react-native
src/lib/stripe.ts
import { StripeProvider, useStripe } from "@stripe/stripe-react-native";
export const StripeWrapper = ({ children }) => (
<StripeProvider
publishableKey="pk_test_..."
merchantIdentifier="merchant.com.irannetworkstate.app"
>
{children}
</StripeProvider>
);
// Payment component
export const PaymentScreen = () => {
const { initPaymentSheet, presentPaymentSheet } = useStripe();
const initializePaymentSheet = async () => {
const { paymentIntent } = await fetch("/api/payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ amount: 2000 }), // $20.00
}).then((res) => res.json());
const { error } = await initPaymentSheet({
merchantDisplayName: "Iran Nation",
paymentIntentClientSecret: paymentIntent.client_secret,
});
if (error) {
console.error("Error initializing payment sheet:", error);
}
};
const handlePayment = async () => {
const { error } = await presentPaymentSheet();
if (error) {
console.error("Payment failed:", error);
} else {
console.log("Payment succeeded!");
}
};
};
Terminal window
# Development (.env.development)
NODE_ENV=development
DATABASE_URL=postgresql://localhost:5432/iran_nation_dev
REDIS_URL=redis://localhost:6379
# Firebase
FIREBASE_PROJECT_ID=iran-nation-dev
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxx@iran-nation-dev.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# Google Services
GOOGLE_MAPS_API_KEY=AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
GOOGLE_OAUTH_CLIENT_ID=xxxxx.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxxx
# Apple Services
APPLE_TEAM_ID=XXXXXXXXXX
APPLE_KEY_ID=XXXXXXXXXX
APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# Expo
EXPO_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Sentry
SENTRY_DSN=https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxx@sentry.io/xxxxxxx
SENTRY_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Email (Resend)
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# AWS S3
AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
AWS_REGION=us-east-1
AWS_S3_BUCKET=iran-nation-storage
# Stripe
STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# URLs
FRONTEND_URL=http://localhost:3000
BACKEND_URL=http://localhost:3001
Terminal window
# Production (.env.production)
NODE_ENV=production
DATABASE_URL=postgresql://prod-db:5432/iran_nation
REDIS_URL=redis://prod-redis:6379
# Use production keys for all services
FIREBASE_PROJECT_ID=iran-nation-prod
GOOGLE_MAPS_API_KEY=AIzaSyPROD_KEY_HERE
SENTRY_DSN=https://prod-dsn@sentry.io/xxxxxxx
RESEND_API_KEY=re_PROD_KEY_HERE
AWS_S3_BUCKET=iran-nation-prod-storage
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Production URLs
FRONTEND_URL=https://irannetworkstate.com
BACKEND_URL=https://api.irannetworkstate.com
Terminal window
# GitHub Secrets (for Actions)
gh secret set FIREBASE_PRIVATE_KEY --body "$(cat firebase-private-key.txt)"
gh secret set GOOGLE_SERVICES_JSON --body "$(cat google-services.json)"
gh secret set APPLE_PRIVATE_KEY --body "$(cat AuthKey_XXXXXXXXXX.p8)"
gh secret set EXPO_TOKEN --body "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
gh secret set SENTRY_AUTH_TOKEN --body "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
gh secret set AWS_ACCESS_KEY_ID --body "AKIAXXXXXXXXXXXXXXXX"
gh secret set AWS_SECRET_ACCESS_KEY --body "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
gh secret set STRIPE_SECRET_KEY --body "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
gh secret set RESEND_API_KEY --body "re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  • Never commit keys to git: Use .env files and .gitignore
  • Rotate keys regularly: Set up key rotation schedules
  • Use environment-specific keys: Separate dev/staging/prod keys
  • Implement key restrictions: Limit API keys to specific IPs/domains
Terminal window
# Principle of least privilege
# Only grant necessary permissions to each service account
# Example: S3 bucket policy for specific operations only
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::iran-nation-storage/uploads/*"
}
]
}
  • Use HTTPS everywhere: All API endpoints and webhooks
  • Implement rate limiting: Prevent abuse of external services
  • Validate webhooks: Verify signatures from external services
  • Monitor usage: Set up alerts for unusual activity
src/health/external-services.health.ts
import { Injectable } from "@nestjs/common";
import { HealthIndicator, HealthIndicatorResult } from "@nestjs/terminus";
@Injectable()
export class ExternalServicesHealthIndicator extends HealthIndicator {
async checkFirebase(): Promise<HealthIndicatorResult> {
try {
// Test Firebase connection
await admin.auth().listUsers(1);
return this.getStatus("firebase", true);
} catch (error) {
return this.getStatus("firebase", false, { error: error.message });
}
}
async checkS3(): Promise<HealthIndicatorResult> {
try {
// Test S3 connection
await this.s3Client.send(
new HeadBucketCommand({ Bucket: this.bucketName })
);
return this.getStatus("s3", true);
} catch (error) {
return this.getStatus("s3", false, { error: error.message });
}
}
async checkStripe(): Promise<HealthIndicatorResult> {
try {
// Test Stripe connection
await this.stripe.balance.retrieve();
return this.getStatus("stripe", true);
} catch (error) {
return this.getStatus("stripe", false, { error: error.message });
}
}
}
  • Set up billing alerts: Monitor service usage costs
  • Implement usage quotas: Prevent unexpected charges
  • Regular cost reviews: Analyze service usage patterns
  • Optimize configurations: Adjust settings based on usage
  • Monitor service announcements: Stay updated on API changes
  • Test in staging first: Always test service updates in non-prod
  • Maintain fallbacks: Implement graceful degradation
  • Document dependencies: Keep track of service versions and configs
Terminal window
# Common Firebase problems and solutions
# 1. Authentication token expired
firebase login --reauth
# 2. Permission denied errors
# Check Firebase security rules and user permissions
# 3. Push notification delivery issues
# Verify APNs certificates and FCM server key
Terminal window
# 1. API quota exceeded
# Check Google Cloud Console quotas and increase if needed
# 2. OAuth consent screen issues
# Verify domain ownership and consent screen configuration
# 3. Maps API not working
# Check API key restrictions and enabled APIs
Terminal window
# 1. Access denied errors
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::account:user/username \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::bucket-name/key-name
# 2. CORS issues
aws s3api put-bucket-cors --bucket bucket-name --cors-configuration file://cors.json
Terminal window
# 1. Webhook signature verification failed
# Verify webhook endpoint URL and secret key
# 2. Payment method declined
# Check Stripe dashboard for detailed error messages
# 3. Test vs live mode confusion
# Ensure you're using the correct API keys for the environment

This comprehensive guide covers all the external services needed for the Iran Nation project. Each service should be configured according to your specific requirements and security policies. Remember to regularly review and update configurations as services evolve and new features are added to the application.