Weiter zum Inhalt
Konto erstellen
oder
anmelden
Das Logo der Stripe-Dokumentation
/
KI fragen
Konto erstellen
Anmelden
Jetzt starten
Zahlungen
Umsatz
Plattformen und Marktplätze
Geldmanagement
Entwickler-Tools
Übersicht
Versionierung
Änderungsprotokoll
Aktualisieren Sie Ihre API-Version
Ihre SDK-Version aktualisieren
Entwickler-Tools
SDKs
API
Tests
Workbench
Ereignisziele
Arbeitsabläufe
Stripe-CLI
Stripe Shell
Entwickler-Dashboard
Agent-Toolkit
Mit LLMs entwickelnStripe für Visual Studio CodeStripe-StatuswarnungenHochgeladene Dateien
Sicherheit und Datenschutz
Sicherheit
Datenschutz
Extend Stripe
Stripe-Apps
    Übersicht
    Jetzt starten
    Eine App erstellen
    Funktionsweise von Stripe-Apps
    Beispiel-Apps
    App erstellen
    Shop-Geheimnisse
    API-Authentifizierungsmethoden
    Autorisierungsabläufe
    Serverseitige Logik
    Ereignisse überwachen
    Umgang mit verschiedenen Modi
    Sandbox-Unterstützung aktivieren
    App-Einstellungsseite
    Erstellen Sie eine Nutzeroberfläche
    Onboarding
    Ihre App verbreiten
    Vertriebsmöglichkeiten
    App hochladen
    Versionen und Releases
    Ihre App testen
    Ihre App veröffentlichen
    Ihre App bewerben
    Deep-Links hinzufügen
    Installationslinks erstellen
    Rollen in Erweiterungen der Nutzeroberfläche zuweisen
    Aktionen nach der Installation
    App-Analytik
    Eingebettete Komponenten für Apps
    Stripe-Apps von Drittanbietern einbetten
    Umstellung auf Stripe Apps
    Migrieren oder Erweiterung erstellen
    Ein Plugin zu Stripe Apps oder Stripe Connect migrieren
    Verwendungszweck
    App-Manifest
    CLI
    Erweiterungs-SDK
    Berechtigungen
    Darstellungsfelder
    Entwurfsmuster
    Komponenten
Stripe Connectors
Partner
Partner-Ecosystem
Partner-Zertifizierung
StartseiteEntwickler-ToolsStripe Apps

Fügen Sie eine serverseitige Logik hinzu

Prüfen und bearbeiten Sie mit dem Backend-Code in Ihrer App Nutzeraktionen und -daten.

Seite kopieren

Mit Stripe Apps können Sie serverseitige Logik mit einem selbst gehosteten Backend hinzufügen. Mit einem selbst gehosteten Backend-Dienst können Sie:

  • Integrieren Sie Ihr Angebot sicher mit Drittanbietersystemen, für die eine serverseitige Integration erforderlich ist.
  • Webhook-Ereignisse von Stripe abonnieren und Stripe mit anderen Systemen synchronisieren.
  • Nutzen Sie langlebige App-Logik, die ausgeführt wird, wenn der/die Nutzer/in den Browser schließt.
  • Erstellen Sie Apps, die eine mit Cron-Jobs vergleichbare Funktionalität aufweisen, um bestimmte Aktionen zu planen.
Ablaufdiagramm des App-Backends

Wie das selbst gehostete Backend mit der App interagiert

Nutzer/innen von Ihrer Nutzeroberfläche im Backend Ihrer App authentifizieren

Um Nutzer/innen über das Dashboard zu authentifizieren, benötigt das Backend eine Signatur mit dem gemeinsam genutzten Geheimschlüssel, das Konto und die Nutzer-ID der aktuellen, angemeldeten Dashboard-Nutzer/innen. Wenn Ihr/e Nutzer/in nicht befugt ist, die API aufzurufen, gibt Stripe einen Berechtigungs-Fehler zurück.

Bevor Sie loslegen

  1. Achten Sie darauf, dass Ihr Backend-Dienst HTTP-Anfragen senden und empfangen kann. Wenn Sie noch keinen API-Server erstellt haben, sollten Sie den Interaktiven Webhook Endpoint Builder ausprobieren.

  2. App hochladen und freigegebenen Geheimschlüssel erstellen:

    Command Line
    stripe apps upload

    Es ist kein Problem, wenn Sie die Entwicklung der aktuellen Version Ihrer App noch nicht abgeschlossen haben. Durch das Hochladen wird Ihre App im Live-Modus nicht aktualisiert.

  3. Erhalten Sie den Geheimschlüssel Ihrer App, um die Signatur in Ihrem Backend zu verifizieren:

    a. Rufen Sie die Detailseite Ihrer Stripe-App auf, indem Sie Ihre App unter Apps auswählen.

    b. Um den Geheimschlüsseldialog zu öffnen, klicken Sie unter der Anwendungs-ID auf das Überlaufmenü () und dann auf Geheimschlüssel für Signatur.

    c. Nun klicken Sie auf das Clipboard , um den Geheimschlüssel aus dem Dialogfeld zu kopieren.

Eine signierte Anfrage senden

Unterschriebene Anfrage senden

So senden Sie eine signierte Anfrage an das Backend der App:

  1. Rufen Sie die aktuelle Signatur mit der asynchronen Funktion fetchStripeSignature ab.
  2. Fügen Sie die Signatur dem Header Stripe-Signature hinzu.
  3. Nehmen Sie die Objekte user_id und account_id in die Anfrage auf.
  4. Überprüfen Sie im Backend der App, ob die Anfrage die Signatur, den Geheimschlüssel der App, user_id und account_id enthält.

Hier ist ein Beispiel für den Versand einer signierten Anfrage mit zusätzlichen Daten.

Eine Beispielanfrage von einer Stripe-App mit dem Header Stripe-Signature:

import {fetchStripeSignature} from '@stripe/ui-extension-sdk/utils'; const App = ({ userContext, environment }: ExtensionContextValue) => { const makeRequestToMyBackend = async (endpoint, requestData) => { // By default the signature is signed with user id and account id. const signaturePayload = { user_id: userContext?.id, account_id: userContext?.account.id, }; return fetch(`https://example.com/${endpoint}/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(), 'Content-Type': 'application/json', }, // Include the account ID and user ID in the body to verify on backend. body: JSON.stringify({ ...requestData, ...signaturePayload, }), }); }; ... }

Beispiel für ein Backend zur Überprüfung der Anfrage:

Bitte beachten Sie, dass die Reihenfolge und Benennung der Nutzlastfelder bei der Signaturüberprüfung wichtig ist. Die user_id steht vor der account_id, und das resultierende Objekt lautet wie folgt: { user_id, account_id }

// Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')(process.env.STRIPE_API_KEY); const express = require('express'); // Find your app's secret in your app settings page in the Developers Dashboard. const appSecret = 'absec_...'; // This example uses Express. const app = require('express')(); app.use(express.json()); // Match the raw body to content type application/json. app.post('/do_secret_stuff', (request, response) => { const sig = request.headers['stripe-signature']; // Retrieve user id and account id from the request body const payload = JSON.stringify({ user_id: request.body['user_id'], account_id: request.body['account_id'] }); try { // Verify the payload and signature from the request with the app secret. stripe.webhooks.signature.verifyHeader(payload, sig, appSecret); } catch (error) { response.status(400).send(error.message); } // Handle the request by returning a response // to acknowledge receipt of the event. response.json({ success: true }); }); app.listen(3000, () => console.log('Running on port 3000'));

Eine signierte Anfrage mit zusätzlichen Daten senden

Die Nutzerauthentifizierung kann auch durch Übermittlung einer signierten Anfrage mit einer Nutzlast (zusätzliche Daten) erfolgen. Wenn Sie die Funktion fetchStripeSignature mit einer Anfrage für eine zusätzliche Nutzlast aufrufen, erstellen Sie eine Signatur mit user_id, account_id und der zusätzlichen Nutzlast, die Sie in die Funktion übergeben haben. Standardmäßig verwenden Stripe-Apps user_id und account_id zum Generieren der Signaturzeichenfolge.

Beispiel für das Generieren eines Geheimschlüssels mit zusätzlicher Nutzlast:

// A valid payload object has keys of type string // and values of type string, number, or boolean. const payload = { "transaction_id": 'ipi_1KRmFUFRwUQjTSJEjRnCCPyV', "amount": 100, "livemode": false, }; fetch(`https://example.com/do_more_secret_stuff/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(payload), 'Content-Type': 'application/json', }, // Append the account ID and user ID in the body to verify on backend. body: JSON.stringify({ ...payload, user_id: 'usr_K6yd2CbXLO9A5G', account_id: 'acct_1JSkf6FRwUQjTSJE', }), });

Beispiel für ein Backend, das die mit zusätzlicher Nutzlast generierte Signatur verifiziert:

// Match the raw body to content type application/json. app.post('/do_more_secret_stuff', (request, response) => { try { // Verify the signature from the header and the request body that // contains the additional data, user ID, and account ID with the app secret. stripe.webhooks.signature.verifyHeader(request.body, sig, appSecret); } catch (error) { response.status(400).send(error.message); } // Handle the request by returning a response // to acknowledge receipt of the event. response.json({ success: true }); });

Nutzerrollen verifizieren (optional)

Sie können die einer bestimmten user_id zugewiesenen Nutzerrollen überprüfen, indem Sie den Schlüssel stripe_roles in die Nutzlast aufnehmen. Geben Sie userContext?.roles an. Dadurch wird eine Liste mit RoleDefinitions zurückgegeben. Wenn eine der Rollen in der Nutzlast keiner user_id zugewiesen ist, gibt fetchStripeSignature einen ungültigen Anfragefehler (400) zurück.

// Provide this special key in the same way you'd // provide any other key to the additional payload. const payload = { "stripe_roles": userContext?.roles, }; fetch(`https://example.com/do_more_secret_stuff/`, { method: 'POST', headers: { 'Stripe-Signature': await fetchStripeSignature(payload), 'Content-Type': 'application/json', }, // Append the account ID and user ID in the body to verify on backend. body: JSON.stringify({ ...payload, user_id: 'usr_K6yd2CbXLO9A5G', account_id: 'acct_1JSkf6FRwUQjTSJE', }), });

Geheimschlüssel ablaufen lassen und erstellen

Wenn Ihr Geheimschlüssel kompromittiert wird, können Sie den Geheimschlüssel Ihrer aktuellen App sofort bis zu 24 Stunden lang ablaufen lassen, um den App-Geheimschlüssel in Ihrem Backend zu aktualisieren. Während dieser Zeit sind zwei Geheimschlüssel für den Endpoint aktiv, der kompromittierte Geheimschlüssel sowie der neu erstellte. Stripe generiert bis zum Ablauf eine Signatur pro Geheimschlüssel.

Einen App-Geheimschlüssel ablaufen lassen und erstellen:

  1. Rufen Sie die Detailseite Ihrer Stripe-App auf, indem Sie Ihre App unter Apps auswählen.
  2. Klicken Sie in der Kopfzeile der Seite unter der Anwendungs-ID auf das Überlaufmenü () und dann auf Geheimschlüssel für Signatur.
  3. Klicken Sie auf Geheimschlüssel ablaufen lassen, um das entsprechende Dialogfeld aufzurufen.
  4. Wählen Sie eine Ablaufdauer für Ihren aktuellen App-Geheimschlüssel aus.
  5. Klicken Sie auf Geheimschlüssel ablaufen lassen.

Umgang mit Cross-Origin Resource Sharing (CORS)

Cross-Origin Resource Sharing (CORS) ist ein wichtiger Bestandteil, um Apps vor Site-übergreifenden Skriptangriffen (XSS) zu schützen. Da Nutzeroberflächen-Erweiterungen für Stripe-Apps zwangsläufig einen Cross-Ursprung und einen Sandbox-iframe haben, müssen Sie ein bestimmtes Verfahren für den Umgang mit Cross-Origin-Anfrage-Headern anwenden.

Damit Ihre Nutzeroberflächen-Erweiterung Daten von Ihrem Backend-Dienst abrufen kann, müssen Sie diesen wie folgt konfigurieren:

  • Anfragen mit der Options-Methode zulassen.
  • Um Anfragen von null-Ursprüngen zuzulassen, setzen Sie Access-Control-Allow-Origin auf *.

Notiz

Erweiterungen der Nutzeroberfläche haben einen Null-Ursprung, da sie aus Sicherheitsgründen in einem geschützten Sandbox-iframe ausgeführt werden.

Viele Backend-Frameworks verfügen über Bibliotheken und Anleitungen, die Ihnen beim Umgang mit CORS helfen. Genauere Anleitungen finden Sie in der Dokumentation zu Ihrem Framework.

Informationen dazu, wie Sie authentifizieren, dass eine Anfrage von Stripe im Namen eines/einer bestimmten Nutzers/in oder eines bestimmten Kontos stammt, finden Sie unter Authentifizieren von Nutzer/innen von Ihrer Nutzeroberfläche bei Ihrem Backend.

Vorsicht

Konfigurieren Sie nur authentifizierte Endpoints und alle Endpoints, mit denen die Erweiterung der Nutzeroberfläche kommuniziert, für die Verwendung von Access-Control-Allow-Origin: *. Nicht authentifizierte Endpoints sind anfällig für CSRF-Angriffe, wenn keine anderen Maßnahmen ergriffen werden.

Stripe-APIs nutzen

Für die Interaktion mit Stripe können Sie Ihre Anfragen an die Stripe-API verwenden und authentifizieren.

Authentifizierungsanfragen

Um Ihre Anfragen zu authentifizieren, verwenden Sie Ihren bestehenden Händlerkonto-API-Schlüssel, um mit Stripe zu interagieren, und geben Sie die stripeAccountId des Nutzers/der Nutzerin an.

Für serverseitige API-Aufrufe können Sie Anfragen als verbundene Konten stellen, indem Sie den speziellen Header Stripe-Account mit der Stripe-Kontokennung (sie beginnt mit dem Präfix acct_) Ihres Plattformnutzers/Ihrer Plattformnutzrin verwenden. Das folgende Beispiel zeigt, wie Sie mit dem geheimen API-Schlüssel Ihrer Plattform und der Konto-ID Ihres Nutzers/Ihrer Nutzerin einen PaymentIntent erstellen.

Command Line
cURL
curl https://api.stripe.com/v1/payment_intents \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -H "Stripe-Account:
{{CONNECTED_ACCOUNT_ID}}
"
\ -d amount=1000 \ -d currency=usd \ -d "payment_method_types[]"=card

Das Stripe-Account-Header-Verfahren wird in jeder API-Anfrage impliziert, die die Stripe-Konto-ID in der URL enthält. Das folgende Beispiel zeigt, wie Sie mit der Konto-ID Ihres/Ihrer Nutzers/in in der URL ein Konto abrufen.

Command Line
cURL
curl https://api.stripe.com/v1/accounts/
{{CONNECTED_ACCOUNT_ID}}
\ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"

Alle serverseitigen Bibliotheken von Stripe unterstützen zudem diese anfragenbasierte Vorgehensweise, wie das folgende Beispiel zeigt:

Ruby
Stripe.api_key = "{{PLATFORM_SECRET_KEY}}" Stripe::Customer.create( {email: 'person@example.edu'}, {stripe_account: '{{CONNECTED_STRIPE_ACCOUNT_ID}}'} ) # Fetching an account just needs the ID as a parameter Stripe::Account.retrieve('{{CONNECTED_STRIPE_ACCOUNT_ID}}')

Selbst gehostetes Backend über Ihre Erweiterung der Nutzeroberfläche aufrufen

Wenn Sie Anfragen von Ihrer Erweiterung der Nutzeroberfläche an Ihr Backend stellen, senden Sie eine Signatur mit Ihrer Anfrage, um die Rechtmäßigkeit der Anfragen zu überprüfen. Übergeben Sie von der Erweiterung der Nutzeroberfläche aus die stripeAccountId für den aktuellen Nutzer/die aktuelle Nutzerin, damit Sie Backend-Anfragen im Namen dieses Nutzers/dieser Nutzerin durchführen können.

// Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')(
'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
); const express = require("express"); const app = express(); app.use(express.static("public")); app.use(express.json()); app.post("/api/data", async (req, res) => { const { stripeAccountId } = req.body; const customer = await stripe.customers.create({ description: 'My First Test Customer (created for API docs)', }, { stripeAccount: stripeAccountId, }); res.send({ data: [] }); }); app.listen(3000, () => console.log("Node server listening on port 3000!"));

Andere APIs aufrufen

Über Ihr selbst gehostetes Backend können Sie jede API aufrufen – Ihre eigene oder eine von anderen Entwickler/innen oder Unternehmen.

Um mehr zu erfahren, machen Sie sich am besten mit dem Speichern geheimer Anmeldedaten und Token vertraut.

Wenn Sie Nutzerinformationen von Stripe an einen anderen Dienst übergeben müssen, verwenden Sie die von der Erweiterung der Nutzeroberfläche übergebene stripeAccountId.

const express = require('express'); const fetch = require('isomorphic-fetch'); const app = express(); app.use(express.static('public')); app.use(express.json()); app.get('/api/time', async (req, res) => { fetch('http://worldclockapi.com/api/json/est/now') .then((response) => response.json()) .then((data) => { res.send({ data: data, }); }); }); app.listen(3000, () => console.log('Node server listening on port 3000!'));

Sie können auch eine Drittanbieter-API über die Erweiterung Ihrer Nutzeroberfläche aufrufen.

Erhalten Sie Ereignisbenachrichtigungen über Ihre App.

Überwachen Sie Ihre Stripe-App mithilfe eingehender Webhooks auf Ereignisse (z. B. Installationen oder Deinstallationen durch Nutzer/innen), damit Ihre Integration automatisch Reaktionen in Ihrem Backend auslösen kann, wie z. B.:

  • Nutzerkonten erstellen
  • Berechtigungen aktualisieren
  • Konto eines/einer Nutzers/in deaktivieren und Daten entfernen

Ereignisse empfangen

Sie können Ereignisse von Stripe für eine App empfangen, die nur für Ihr Konto bestimmt ist, oder für eine App, die im App-Marktplatz aufgeführt ist:

So empfangen Sie Ereignisse für eine App, die öffentlich im App-Marktplatz aufgeführt ist:

  1. Verarbeiten Sie Webhook-Ereignisse im Backend Ihrer App.
  2. Registrieren Sie einen Webhook-Endpoint im Stripe-Dashboard und wählen Sie bei der Registrierung Ihres Webhook-Endpoints die Option Überwachen Sie Ereignisse von verbundenen Konten aus.
  3. Fügen Sie Ihrer App die Berechtigung event_read hinzu:
    Command Line
    stripe apps grant permission "event_read" "Allows reading event data from users who have installed the app"
  4. Fügen Sie für jedes Ereignis, das Ihr Webhook-Endpoint überwacht, die entsprechende Berechtigung hinzu:
    Command Line
    stripe apps grant permission "PERMISSION_NAME" "EXPLANATION"
    Ersetzen:
    • PERMISSION_NAMEdurch den Berechtigungsnamen für ein Ereignis.
    • EXPLANATION durch eine Erklärung für die Aktivierung des Zugriffs. Nutzer/innen sehen diese Erklärung, wenn sie Ihre App installieren. Beispiel: „Erlaubt das Lesen von Ereignisdaten von Nutzer/innen, die die App installiert haben.“

Wenn ein Händler ein Ereignis auslöst, stellt Stripe das folgende Event-Objekt bereit. Dieses Ereignis enthält die Eigenschaft account, die die Konto-ID des Händlers angibt, der das Ereignis auslöst:

{ "id": "evt_70xqb7js6lGOmK", "livemode": true, "object": "event", "type": "account.application.authorized", "account": "acct_Tkp6qj7M4Ys5yu", "pending_webhooks": 2, "created": 1349654313, "data": {...} }

Mit dem Attribut account können Sie Folgendes durchführen:

  • Überwachen Sie, wie viele Händler Ihre App installieren und deinstallieren.
  • Führen Sie mit Stripe Connect API-Aufrufe im Auftrag von Nutzer/innen durch.

Ereignisse für Stripe-Apps

Zusätzlich zu den Ereignistypen, die Stripe unterstützt, unterstützt Stripe Apps auch die folgenden Ereignisse:

HändleraktionDas resultierende Webhook-Ereignis wurde an das Backend der App gesendet
App verbinden oder installierenaccount.application.authorized
Verbindung zur App trennen oder App deinstallierenaccount.application.deauthorized

Das Ereignisverhalten hängt vom Installationsmodus ab

Ihre Nutzer/innen können Apps im Live-Modus, im Test-Modus, in beiden Modi oder in einer Sandbox-Umgebung installieren. Richten Sie Webhooks gemäß den folgenden Richtlinien ein:

  • Wenn die App in einer Sandbox-Umgebung installiert ist, werden Ereignisse nur an die Sandbox-Umgebung gesendet.
  • Wenn die App nur im Live-Modus installiert wird, werden Live-Modus-Ereignisse an den Live-Modus-Endpoint gesendet.
  • Wenn die App nur im Test-Modus installiert wird, werden Test-Modus-Ereignisse an den Test-Modus-Endpoint gesendet.
  • Wenn die App in beiden Modi installiert ist, werden Test-Modus-Ereignisse sowohl an die Test-Modus- als auch an die Live-Modus-Endpoints und Live-Modus-Ereignisse an Live-Modus-Endpoints gesendet.

Konfigurieren Sie den Connect /webhook für den Live- und den Test-Modus und verwenden Sie dann das folgende Snippet für beide Modi der App. Ein vollständiges Endpoint-Beispiel finden Sie in der Dokumentation zu Webhooks.

Ruby
require 'sinatra' require 'json' post '/webhook' do event = JSON.parse(request.body.read) if event['livemode'] puts "Handling live event: #{event}" # Handle live events handle_live_event(event) else puts "Handling test event: #{event}" # Handle test events handle_test_event(event) end status 200 body 'Event received' end

Fehlerbehebung

Wenn Sie nicht die erwarteten Ereignisse erhalten, überprüfen Sie Ihre Konfiguration auf folgende häufige Fehler:

  • Stellen Sie sicher, dass für Webhooks im Live-Modus Live-Modus-Schlüssel und für Webhooks im Test-Modus Test-Modus-Schlüssel verwendet werden.
  • Stellen Sie bei Live-Modus-Ereignissen sicher, dass das installierende Konto aktiviert ist.
  • Stellen Sie sicher, dass Ihre App sowohl mit Live-Modus- als auch mit Test-Modus-Ereignissen umgehen kann.
  • Durch das Auslösen von Testereignissen wird das Verhalten eines Live-Ereignisses nicht repliziert, es sei denn, dies wurde ausdrücklich in der App-Konfiguration eingerichtet.

Webhooks lokal testen

Sie können Ihre Webhooks lokal auf Folgendes testen:

  • Eine App, die nur für Ihre Nutzer/innen verfügbar ist und Ihre Kontoereignisse überwacht
  • Eine App, die auf dem Stripe App Marketplace verfügbar ist und Ereignisse auf Konten überwacht, auf denen Ihre App installiert ist

So testen Sie Webhooks lokal:

  1. Installieren Sie die Stripe-CLI.

  2. Authentifizieren Sie Ihr Konto:

    Command Line
    stripe login
  3. Öffnen Sie zwei Terminal-Fenster:

    • In einem Terminal-Fenster können Sie die Ereignisweiterleitung einrichten:

      Command Line
      stripe listen --forward-to localhost:{{PORT}}/webhook
    • In dem anderen Terminal-Fenster können Sie Ereignisse zum Testen Ihrer Webhooks-Integration auslösen:

      Command Line
      stripe trigger {{EVENT_NAME}}

Weitere Informationen finden Sie in unserer Dokumentation zum Testen eines Webhook-Endpoints.

Siehe auch

  • Nutzeroberfläche erstellen
  • App hochladen und installieren
  • App veröffentlichen
War diese Seite hilfreich?
JaNein
Benötigen Sie Hilfe? Kontaktieren Sie den Kundensupport.
Nehmen Sie an unserem Programm für frühzeitigen Zugriff teil.
Schauen Sie sich unser Änderungsprotokoll an.
Fragen? Sales-Team kontaktieren.
LLM? Lesen Sie llms.txt.
Unterstützt von Markdoc