{"openapi":"3.1.0","info":{"title":"ILYGO Pay — API","version":"1.0.0","description":"Service de paiement / facturation / licences centralisé d'ILYGO (Stripe). Endpoints serveur-à-serveur (S2S) consommés par les apps ILYGO. Authentification par token d'application `ilypay_…` (Bearer). Mode test : utiliser une clé Stripe de test côté serveur et un token d'app émis sur l'environnement UAT.","contact":{"name":"ILYGO","url":"https://ilygo.ch","email":"info@ilygo.ch"}},"servers":[{"url":"https://0.0.0.0:3000","description":"Cet environnement"},{"url":"https://api.pay.uat.ilygo.ch","description":"UAT (test)"}],"security":[{"appToken":[]}],"components":{"securitySchemes":{"appToken":{"type":"http","scheme":"bearer","description":"Token d'application `ilypay_…` émis par un platform-admin. En-tête `Authorization: Bearer ilypay_…`."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"issues":{"type":"array","items":{}}},"required":["error"]},"BillTo":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"address":{"type":"object","properties":{"line1":{"type":"string"},"line2":{"type":"string"},"postal_code":{"type":"string"},"city":{"type":"string"},"country":{"type":"string","minLength":2,"maxLength":2}}}}},"OpenPaymentRequest":{"type":"object","required":["user_sub","tenant","type","amount","currency","title"],"properties":{"user_sub":{"type":"string","description":"Sub Keycloak du payeur."},"tenant":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"type":{"type":"string","enum":["oneshot","recurring"]},"amount":{"type":"integer","description":"Centimes, TTC (TVA CH 8.1% inclusive).","maximum":10000000},"currency":{"type":"string","enum":["chf","eur"]},"interval":{"type":"string","enum":["month","year"],"description":"Requis si recurring."},"title":{"type":"string"},"description":{"type":"string"},"license_id":{"type":"string","format":"uuid","description":"Optionnel ; généré sinon."},"locale":{"type":"string","enum":["fr","de","en","it"]},"collection_method":{"type":"string","enum":["card","bank_transfer"],"description":"card (défaut) ou bank_transfer (facture par virement / QR-facture)."},"bill_to":{"$ref":"#/components/schemas/BillTo"}}},"OpenPaymentResponse":{"type":"object","properties":{"license_id":{"type":"string","format":"uuid"},"status":{"type":"string","example":"pending"},"collection_method":{"type":"string","enum":["card","bank_transfer"]}}},"CheckoutSessionRequest":{"type":"object","required":["tenant","license_id","type","amount","currency","description","return_url"],"properties":{"tenant":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"license_id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["oneshot","recurring"]},"amount":{"type":"integer","maximum":10000000},"currency":{"type":"string","enum":["chf","eur"]},"interval":{"type":"string","enum":["month","year"]},"title":{"type":"string"},"description":{"type":"string"},"return_url":{"type":"string","format":"uri","description":"Doit être dans l'allowlist de l'app."}}},"CheckoutSessionResponse":{"type":"object","properties":{"redirect_url":{"type":"string","format":"uri","nullable":true}}},"License":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","active","past_due","canceled","expired","abandoned"]},"type":{"type":"string","enum":["oneshot","recurring"]},"current_period_end":{"type":"string","format":"date-time","nullable":true},"cancel_at_period_end":{"type":"boolean"}}}}},"paths":{"/v1/payments":{"post":{"summary":"Ouvrir un paiement (facture en attente)","description":"Crée une licence `pending` rattachée à (app, tenant, user). Apparaît « À régler » dans le portail du payeur, qui la règle ensuite. Aucun débit immédiat.","operationId":"openPayment","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenPaymentRequest"}}}},"responses":{"201":{"description":"Paiement ouvert","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenPaymentResponse"}}}},"400":{"description":"Corps invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Token d'app manquant/invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Stripe non configuré","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/checkout-sessions":{"post":{"summary":"Ouvrir + démarrer un Checkout (carte)","description":"Ouvre un paiement et crée immédiatement une session Stripe Checkout. Renvoie l'URL de redirection. Auth : token Keycloak échangé (azp=app).","operationId":"createCheckoutSession","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionRequest"}}}},"responses":{"200":{"description":"Session créée","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionResponse"}}}},"400":{"description":"Corps invalide / return_url non autorisé","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Auth invalide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/licenses/{id}":{"get":{"summary":"Vérifier une licence (gate-check)","description":"Renvoie l'état d'une licence. Scopé `azp` : 403 si la licence n'appartient pas à l'app appelante.","operationId":"getLicense","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Licence","content":{"application/json":{"schema":{"$ref":"#/components/schemas/License"}}}},"403":{"description":"La licence n'appartient pas à l'app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Introuvable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/licenses/batch":{"post":{"summary":"Vérifier des licences en lot (gate-check)","description":"Renvoie l'état des licences appartenant à l'app appelante (scopé azp).","operationId":"getLicenses","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":100}}}}}},"responses":{"200":{"description":"Licences","content":{"application/json":{"schema":{"type":"object","properties":{"licenses":{"type":"array","items":{"$ref":"#/components/schemas/License"}}}}}}}}}},"/v1/quotes":{"post":{"summary":"Créer un devis (proforma) B2B","description":"Crée et finalise un Stripe Quote pour un (app, tenant, user).","operationId":"createQuote","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_sub","tenant","amount","currency","title"],"properties":{"user_sub":{"type":"string"},"tenant":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"amount":{"type":"integer","maximum":10000000},"currency":{"type":"string","enum":["chf","eur"]},"title":{"type":"string"},"description":{"type":"string"},"expires_days":{"type":"integer"}}}}}},"responses":{"201":{"description":"Devis créé","content":{"application/json":{"schema":{"type":"object","properties":{"quote_id":{"type":"string"},"status":{"type":"string"},"pdf":{"type":"string"}}}}}}}}}}}