Integration Guide
Guida pratica per integrare il billing-service con gli altri componenti del sistema.
π Integrazione Backendβ
Verificare Abbonamento da Altri Serviziβ
Il modo piΓΉ semplice per verificare se un utente ha un abbonamento attivo:
import httpx
async def check_user_subscription(user_id: int) -> bool:
"""Check if user has active subscription."""
try:
response = await httpx.AsyncClient().get(
f"http://billing:8088/api/subscriptions/check/{user_id}"
)
data = response.json()
return data.get("is_subscribed", False)
except Exception:
# Fallback: assume not subscribed if billing is down
return False
Consumare Eventi Redisβ
Per reagire ai cambi di stato in tempo reale:
import redis
import json
def consume_billing_events():
"""Consume subscription events from Redis stream."""
r = redis.Redis.from_url("redis://redis:6379", decode_responses=True)
# Create consumer group if not exists
try:
r.xgroup_create("billing:events", "my-service-consumers", id='0', mkstream=True)
except redis.ResponseError as e:
if "BUSYGROUP" not in str(e):
raise
while True:
entries = r.xreadgroup(
"my-service-consumers",
"consumer-1",
{"billing:events": '>'},
count=10,
block=5000
)
for stream, messages in entries:
for msg_id, data in messages:
event = json.loads(data['event'])
handle_subscription_event(event)
r.xack("billing:events", "my-service-consumers", msg_id)
def handle_subscription_event(event: dict):
"""Handle subscription state change."""
user_id = int(event['userId'])
new_status = event['newStatus']
if new_status == 'EXPIRED':
disable_premium_features(user_id)
elif new_status == 'ACTIVE':
enable_premium_features(user_id)
π± Integrazione Mobileβ
iOS - StoreKit 2β
Quando crei un acquisto, passa lo user_id come appAccountToken:
// VislaGPS/StoreManager.swift
import StoreKit
func purchase(product: Product, userId: Int) async throws {
// Pass user_id as appAccountToken (UUID format required)
let uuid = UUID(uuidString: String(format: "%08x-0000-0000-0000-000000000000", userId))!
let result = try await product.purchase(options: [
.appAccountToken(uuid)
])
// Handle result...
}
Android - Google Play Billingβ
Passa l'ID utente come obfuscatedAccountId:
// SubscriptionManager.kt
import com.android.billingclient.api.*
fun launchPurchase(activity: Activity, productDetails: ProductDetails, userId: String) {
val flowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
))
.setObfuscatedAccountId(userId) // Pass user ID
.build()
billingClient.launchBillingFlow(activity, flowParams)
}
π³ Integrazione Stripe (Web)β
Creare Subscription con Metadataβ
Quando crei una subscription via Stripe Checkout:
import stripe
def create_checkout_session(user_id: int, price_id: str):
"""Create Stripe checkout session with user metadata."""
session = stripe.checkout.Session.create(
mode="subscription",
line_items=[{"price": price_id, "quantity": 1}],
success_url="https://app.vislagps.com/success",
cancel_url="https://app.vislagps.com/cancel",
client_reference_id=str(user_id),
subscription_data={
"metadata": {
"user_id": str(user_id) # IMPORTANTE!
}
}
)
return session
Importante
Il campo metadata.user_id Γ¨ essenziale. Senza di esso, il billing-service non puΓ² associare la subscription all'utente.
π Configurazione Appleβ
App Store Connectβ
- Vai su App Store Connect β My Apps β La tua app
- App Information β App Store Server Notifications
- Imposta:
- Production URL:
https://gateway.vislagps.com/webhooks/apple - Sandbox URL:
https://gateway-dev.vislagps.com/webhooks/apple - Version: 2 (v2 S2S)
- Production URL:
Generare Shared Secretβ
- App Store Connect β Users and Access β Keys
- Genera una chiave e salva in
secrets/apple_shared_secret
π€ Configurazione Googleβ
Google Cloud Consoleβ
- Vai su Google Cloud Console β Pub/Sub
- Crea topic per notifiche
- Crea subscription push:
- Endpoint:
https://gateway.vislagps.com/webhooks/google
- Endpoint:
Google Play Consoleβ
- Vai su Google Play Console β API access
- Collega il progetto Google Cloud
- Imposta Real-time developer notifications:
- Topic: il topic Pub/Sub creato
Service Accountβ
- Crea service account con ruolo
Pub/Sub Subscriber - Esporta JSON e salva in
secrets/google_service_account_billing.json
β‘ Configurazione Stripeβ
Dashboard Stripeβ
- Vai su Stripe Dashboard β Developers β Webhooks
- Aggiungi endpoint:
- URL:
https://gateway.vislagps.com/webhooks/stripe - Eventi:
customer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.paidinvoice.payment_failed
- URL:
- Copia Signing secret e salva in
secrets/stripe_webhook_secret
Test Locale con Stripe CLIβ
# Installa Stripe CLI
brew install stripe/stripe-cli/stripe
# Login
stripe login
# Forward webhooks a localhost
stripe listen --forward-to localhost:8088/webhooks/stripe
# In altro terminale, trigger eventi di test
stripe trigger customer.subscription.created
π§ͺ Testingβ
Test Health Checkβ
curl http://localhost:8088/health
Test Subscription Checkβ
curl "http://localhost:8088/api/subscriptions/check/123"
Test Webhook con Payload Mockβ
curl -X POST http://localhost:8088/webhooks/stripe \
-H "Content-Type: application/json" \
-H "Stripe-Signature: t=123,v1=xxx" \
-d '{"id": "evt_test", "type": "customer.subscription.created", ...}'
tip
Il signature sarΓ invalido senza il vero secret. Per test locali, puoi temporaneamente disabilitare la validazione o usare Stripe CLI.