Skip to main content

Billing Service API

Base URLโ€‹

/api (via gateway) o http://billing:8088 (interno)

๐Ÿ“˜ TypeScript Interfacesโ€‹

// Subscription Object
interface Subscription {
id: number;
user_id: number;
provider: 'stripe' | 'apple' | 'google';
plan_id: string;
plan_name: string | null;
status: SubscriptionStatus;
current_period_start: string | null; // ISO 8601
current_period_end: string | null; // ISO 8601
canceled_at: string | null; // ISO 8601
created_at: string; // ISO 8601
}

// Normalized subscription status
type SubscriptionStatus =
| 'ACTIVE'
| 'EXPIRED'
| 'GRACE_PERIOD'
| 'CANCELED'
| 'PAST_DUE';

// Get Subscriptions Response
interface GetSubscriptionsResponse {
user_id: number;
subscriptions: Subscription[];
has_active_subscription: boolean;
}

// Check Subscription Response
interface CheckSubscriptionResponse {
user_id: number;
is_subscribed: boolean;
status: SubscriptionStatus | null;
provider: string | null;
plan_id: string | null;
expires_at: string | null; // ISO 8601
}

// Webhook Response
interface WebhookResponse {
status: 'processed' | 'duplicate' | 'skipped';
subscription_id?: number;
subscription_status?: SubscriptionStatus;
reason?: string;
}

// Health Response
interface HealthResponse {
status: 'ok' | 'degraded';
service: 'billing';
database?: 'connected' | string;
}

๐Ÿ“ก Endpointsโ€‹

1. Subscription APIโ€‹

Get User Subscriptionsโ€‹

GET /api/subscriptions/{user_id}

Restituisce tutte le subscription di un utente.

Response: GetSubscriptionsResponse

{
"user_id": 123,
"subscriptions": [
{
"id": 1,
"user_id": 123,
"provider": "stripe",
"plan_id": "price_xxx",
"plan_name": "Pro Monthly",
"status": "ACTIVE",
"current_period_start": "2025-12-15T00:00:00Z",
"current_period_end": "2026-01-15T00:00:00Z",
"canceled_at": null,
"created_at": "2025-12-15T10:00:00Z"
}
],
"has_active_subscription": true
}

Check Subscription Statusโ€‹

GET /api/subscriptions/check/{user_id}

Check veloce per service-to-service calls.

Response: CheckSubscriptionResponse

{
"user_id": 123,
"is_subscribed": true,
"status": "ACTIVE",
"provider": "stripe",
"plan_id": "price_xxx",
"expires_at": "2026-01-15T00:00:00Z"
}

Uso tipico:

# Dal device-service, prima di restituire dati premium
response = httpx.get(f"http://billing:8088/api/subscriptions/check/{user_id}")
if response.json()["is_subscribed"]:
# Mostra contenuti premium

Get by Provider IDโ€‹

GET /api/subscriptions/by-provider/{provider}/{subscription_id}

Cerca subscription per ID del provider (debug/admin).

Esempio:

GET /api/subscriptions/by-provider/stripe/sub_xxx

Response: Subscription o 404


2. Webhook Endpointsโ€‹

Importante

I webhook NON richiedono autenticazione JWT ma validano la firma del provider.

Stripe Webhookโ€‹

POST /webhooks/stripe

Riceve eventi Stripe (subscription, invoice).

Headers richiesti:

  • Stripe-Signature: Firma HMAC

Eventi gestiti:

  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.paid
  • invoice.payment_failed

Response:

{
"status": "processed",
"subscription_id": 1,
"subscription_status": "ACTIVE"
}

Apple Webhookโ€‹

POST /webhooks/apple

Riceve App Store Server Notifications v2.

Body:

{
"signedPayload": "eyJhbGciOiJFUzI1NiJ9..."
}

Eventi gestiti:

  • SUBSCRIBED
  • DID_RENEW
  • DID_FAIL_TO_RENEW
  • EXPIRED
  • DID_CHANGE_RENEWAL_STATUS
  • E altri...

Google Webhookโ€‹

POST /webhooks/google

Riceve Real-time Developer Notifications via Pub/Sub.

Body (Pub/Sub push):

{
"message": {
"messageId": "xxx",
"data": "base64_encoded_notification"
}
}

Notification types gestiti:

TypeNomeStato risultante
1SUBSCRIPTION_RECOVEREDACTIVE
2SUBSCRIPTION_RENEWEDACTIVE
3SUBSCRIPTION_CANCELEDCANCELED
4SUBSCRIPTION_PURCHASEDACTIVE
5SUBSCRIPTION_ON_HOLDPAST_DUE
6SUBSCRIPTION_IN_GRACE_PERIODGRACE_PERIOD
12SUBSCRIPTION_REVOKEDEXPIRED
13SUBSCRIPTION_EXPIREDEXPIRED

3. Health Checkโ€‹

Healthโ€‹

GET /health

Response:

{
"status": "ok",
"service": "billing",
"database": "connected"
}

๐Ÿ”Œ Integrazione Frontendโ€‹

Verificare se utente รจ abbonatoโ€‹

async function checkSubscription(userId: number): Promise<boolean> {
const response = await fetch(
`${API_BASE_URL}/api/subscriptions/check/${userId}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);

const data = await response.json();
return data.is_subscribed;
}

Mostrare dettagli abbonamentoโ€‹

async function getSubscriptionDetails(userId: number): Promise<Subscription[]> {
const response = await fetch(
`${API_BASE_URL}/api/subscriptions/${userId}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);

const data = await response.json();
return data.subscriptions;
}

โš ๏ธ Error Codesโ€‹

StatusSignificato
200Successo
400Payload invalido o firma errata
404Subscription non trovata
500Errore interno