# Zahlungen für digitale Waren auf iOS mit benutzerdefiniertem Bezahlvorgang akzeptieren Öffnen Sie Ihren eigenen benutzerdefinierten Bezahlvorgang, um digitale In-App-Waren und Abonnements mit Payment Element zu verkaufen. Für digitale Produkte, Inhalte und Abos, die in den Vereinigten Staaten oder im Europäischen Wirtschaftsraum (EWR) verkauft werden, kann Ihre iOS-App Apple Pay über [Elements](https://docs.stripe.com/payments/elements.md) akzeptieren. Wenn Sie eine begrenzte Anzahl von Produkten und Preisen haben, können Sie stattdessen [Payment Links](https://docs.stripe.com/mobile/digital-goods/payment-links.md) verwenden. In anderen Regionen kann Ihre App Apple Pay für digitale Produkte, Inhalte oder Abos nicht akzeptieren. In diesem Leitfaden wird beschrieben, wie Sie ein Abo in Ihrer App verkaufen, indem Sie Elements verwenden, um Ihre Kundinnen und Kunden auf Ihre eigene Checkout-Seite weiterzuleiten. Wenn Sie bereits eine eigene Checkout-Seite verwenden, die Elements nutzt, können Sie direkt zum Schritt [Universelle Links einrichten](https://docs.stripe.com/mobile/digital-goods/custom-checkout.md#universal-links) springen. > Sollten Sie als Unternehmen neu bei Stripe sein, ein hohes Zahlungsvolumen verarbeiten und erweiterte Integrationsanforderungen haben, [wenden Sie sich an unser Sales-Team](https://stripe.com/contact/sales) ## Sie werden Folgendes entwickeln Wenn Ihre Integration [kundenseitig konfigurierte Konten](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) verwendet, ersetzen Sie in den Codebeispielen Verweise auf `Customer` und Ereignisse durch die entsprechenden Verweise auf die Accounts v2 API. Weitere Informationen finden Sie unter [Kundinnen und Kunden mit Kontoobjekten darstellen](https://docs.stripe.com/connect/use-accounts-as-customers.md). Dieser Leitfaden bietet Informationen zu den folgenden Vorgehensweisen: - Erfassen Sie Zahlungsinformationen mit Ihrer eigenen Zahlungsseite mithilfe von [Elements](https://docs.stripe.com/payments/elements.md). - Modellieren Sie Ihre Abonnements mit *Produkten* (Products represent what your business sells—whether that's a good or a service), *Preisen* (Prices define how much and how often to charge for products. This includes how much the product costs, what currency to use, and the interval if the price is for subscriptions) und *Kundinnen/Kunden* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments). - Verwenden Sie *Universelle Links* (Use Universal links on iOS and macOS to link directly to in-app content. They're standard HTTPS links, so the same URL works for your website and your app), um von Checkout direkt an Ihre App weiterzuleiten. - Überwachen Sie *Webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests), um die In-App-Abonnements Ihrer Kundinnen und Kunden zu aktualisieren. Dieser Leitfaden beschreibt nur den Prozess für den Verkauf digitaler Güter in der App. Wenn Sie eines der folgenden Produkte verkaufen, verwenden Sie stattdessen den [Leitfaden für native iOS-Zahlungen](https://docs.stripe.com/payments/mobile.md): - Physische Waren - Waren und Dienste zum Verbrauch außerhalb Ihrer App - Echtzeit-Dienste von Person zu Person ## Stripe einrichten [Serverseitig] [Registrieren Sie sich](https://dashboard.stripe.com/register) zunächst für ein Stripe-Konto. Fügen Sie dann die Stripe API-Bibliothek Ihrem Backend hinzu: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` Installieren Sie als Nächstes die Stripe CLI. Die CLI bietet die erforderlichen [Webhook](https://docs.stripe.com/webhooks.md#test-webhook)-Tests und kann zum Erstellen Ihrer Produkte und Preise ausgeführt werden. Weitere Installationsoptionen finden Sie unter [Mit der Stripe-CLI loslegen](https://docs.stripe.com/stripe-cli.md). ## Produkte und Preise erstellen Erstellen Sie Ihre Produkte und ihre Preise im Dashboard oder mit der Stripe CLI. Dieses Beispiel verwendet ein Produkt und einen Preis, um ein Abo-Produkt mit einem monatlichen Preis von 9,99 USD darzustellen. 1. Navigieren Sie zur Seite [Produkt hinzufügen](https://dashboard.stripe.com/test/products/create) und erstellen Sie ein Abonnementprodukt mit einem monatlichen Preis von 9,99 USD. 1. Zeichnen Sie nach Erstellung des Preises die Preis-ID auf, sodass diese in nachfolgenden Schritten verwendet werden kann. Preis-IDs sehen in etwa wie folgt aus: `price_G0FvDp6vZvdwRZ`. 1. Klicken Sie als Nächstes auf **In Live-Modus kopieren**, um Ihr Produkt aus einer [Testumgebung in den Live-Modus zu klonen](https://docs.stripe.com/keys.md#test-live-modes). ## Kundinnen/Kunden erstellen [Serverseitig] Jedes Mal, wenn Ihre Kundin/Ihre Kunde Ihre Bezahlseite aufruft, erstellen Sie ein Customer-Objekt für ihn/sie, sofern noch keines existiert. Ihr Server muss Folgendes verarbeiten: - Kundenerstellung (falls es noch keine/n passende/n Stripe-Kunden/-Kundin gibt) - Die Abonnementerstellung befindet sich im Status `incomplete`. - Das Client-Geheimnis des PaymentIntent an das Frontend zurückgeben. - Webhook-Verarbeitung, damit Sie den Abonnementstatus Ihrer Kundinnen/Kunden in Ihrer eigenen Datenbank aktualisieren können. #### Node.js ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. // Find your keys at https://dashboard.stripe.com/apikeys. const stripe = require('stripe')('<>'); // This assumes your app has an existing user database, which we'll call `myUserDB`. const user = myUserDB.getUser("jennyrosen"); if (!user.stripeCustomerID) { const customer = await stripe.customers.create({ name: user.name, email: user.email, }); // Set the user's Stripe Customer ID for later retrieval. user.stripeCustomerID = customer.id; } ``` > Speichern Sie eine Zuweisung auf Ihrem Server zwischen dem Benutzerkonto und der Stripe-Kunden-ID. Ohne eine solche Verbindung zur Zuordnung einer Kundin/eines Kunden zu einem Kauf, können Ihre Kundinnen und Kunden ihre Käufe nicht erhalten. > > Ändert ein Kunde/eine Kundin seine/ihre E-Mail-Adresse auf der Checkout-Seite, aktualisiert sich das Kundenobjekt mit der neuen E-Mail-Adresse. ## Abonnement erstellen [Serverseitig] Beim Erstellen eines Abonnements zur Verwendung des Payment Element übergeben Sie üblicherweise `payment_behavior: 'default_incomplete'`. Hierdurch wird Stripe angewiesen, ein Abonnement mit dem Status `incomplete` zu erstellen und einen Payment Intent für die erste Zahlung zu generieren. > Speichern Sie die `subscription.id` in Ihrer Datenbank, um zukünftige Abonnementereignisse wie Kündigungen, Upgrades und Downgrades zu verwalten. #### Node.js ```javascript // This example sets up an endpoint using the Express framework. const express = require('express'); const app = express(); // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require('stripe')('<>'); app.post('/create-subscription', async (req, res) => { const { priceId, customerId } = req.body; // Create the subscription // setting payment_behavior to "default_incomplete" ensures we get a Payment Intent // that we can confirm on the client using the Payment Element const subscription = await stripe.subscriptions.create({ customer: customerId, items: [{ price: priceId }], payment_behavior: 'default_incomplete', expand: ['latest_invoice.payment_intent'], }); // Make sure you associate the subscription ID with the user in your database! myUserDB.addUserSubscription("jennyrosen", subscription.id); // Get the Payment Intent client secret const paymentIntent = subscription.latest_invoice.payment_intent; const clientSecret = paymentIntent.client_secret; return res.json({ subscriptionId: subscription.id, clientSecret: clientSecret, }); }); app.post('/login', async (req, res) => { // This assumes your app has an existing user database, which we'll call `myUserDB`. const token = myUserDB.login(req.body.login_details) res.json({token: token}) }); app.listen(4242, () => console.log(`Listening on port ${4242}!`)); ``` > Apple Pay ist [standardmäßig aktiviert](https://dashboard.stripe.com/settings/payment_methods) und wird automatisch im Payment Element angezeigt, wenn ein Kunde/eine Kundin ein unterstütztes Gerät verwendet und mindestens eine Karte in der Wallet-App gespeichert hat. Mit der Eigenschaft `payment_method_types` können Sie zusätzliche Zahlungsmethoden akzeptieren. Weitere Details finden Sie in der [Übersicht über die Zahlungsmethoden](https://docs.stripe.com/payments/payment-methods/overview.md). ## Universelle Links einrichten Universelle Links ermöglichen es Ihrer Bezahlseite einen Deep Link zu Ihrer App zu erstellen. So konfigurieren Sie einen universellen Link: - Fügen Sie Ihrer Domain eine `apple-app-site-association`-Datei hinzu. - Fügen Sie Ihrer App eine Berechtigung für die zugewiesenen Domains (“Associated Domains”) hinzu. - Fügen Sie eine Fallbackseite für Ihre Checkout-Umleitungs-URL hinzu. #### Definieren Sie die zugewiesenen Domains Fügen Sie Ihrer Domain eine Datei unter `.well-known/apple-app-site-association` hinzu, um die URLs zu definieren, die Ihre App verarbeitet. Stellen Sie Ihrer App-ID die Team-ID voran, die Sie auf der [Mitgliedschafts-Seite des Apple Developer Portals](https://developer.apple.com/account) finden. ```json { "applinks": { "apps": [], "details": [ { "appIDs": [ "A28BC3DEF9.com.example.MyApp1", "A28BC3DEF9.com.example.MyApp1-Debug" ], "components": [ { "/": "/checkout_redirect*", "comment": "Matches any URL whose path starts with /checkout_redirect" } ] } ] } } ``` Sie müssen der Datei den MIME-Typ `application/json` hinzufügen. Mit `curl -I` bestätigen Sie den Inhaltstyp. ```bash curl -I https://example.com/.well-known/apple-app-site-association ``` Auf der Seite [Zugewiesene Domains unterstützen](https://developer.apple.com/documentation/xcode/supporting-associated-domains) von Apple finden Sie weitere Einzelheiten hierzu. #### Fügen Sie Ihrer App eine Berechtigung für die zugewiesenen Domains (“Associated Domains”) hinzu. 1. Öffnen Sie den Bereich **Signierung & Kapazitäten** der Zielanwendung Ihrer App. 1. Klicken Sie auf **+ Funktion** und wählen Sie dann **Zugewiesene Domains** aus. 1. Fügen Sie der Liste der zugewiesenen Domains einen Eintrag für `applinks:example.com` hinzu. Weitere Informationen zu universellen Links finden Sie auf der Seite [Universelle Links](https://developer.apple.com/ios/universal-links/) von Apple. Obgleich iOS Links an die in Ihrer `apple-app-site-association`-Datei definierten URLs abruft, kann es zu Situationen kommen, in denen die Umleitung Ihre App nicht öffnen kann. Erstellen Sie eine [Fallback-Seite](https://docs.stripe.com/payments/checkout/custom-success-page.md) auf Ihren `success`- und `cancel`-URLs. Sie können zum Beispiel eine `/checkout_redirect/success`-Seite und eine `/checkout_redirect/cancel`-Seite haben. ## Checkout in Safari öffnen [Clientseitig] Fügen Sie Ihrer App eine Checkout-Schaltfläche hinzu. Mit dieser Schaltfläche wird Ihre nutzerdefinierte Bezahlseite in Safari geöffnet. ```swift import Foundation import SwiftUI import StoreKit struct BuySubscriptionsView: View { @EnvironmentObject var myBackend: MyBackend @State var paymentComplete = false var body: some View { // Check if payments are blocked by Parental Controls on this device. if !SKPaymentQueue.canMakePayments() { Text("Payments are disabled on this device.") } else { if paymentComplete { Text("Payment complete!") } else { Button { UIApplication.shared.open("https://example.com/checkout", options: [:], completionHandler: nil) } label: { Text("Subscribe") }.onOpenURL { url in // Handle the universal link from Checkout. if url.absoluteString.contains("success") { // The payment was completed. Show a success // page and fetch the latest customer entitlements // from your server. paymentComplete = true } } } } } } ``` ## Zu Ihrer App zurückleiten [Serverseitig] Stellen Sie bei Verwendung von Elements sicher, dass Sie Nutzer/innen nach einer erfolgreichen Zahlungsbestätigung (über den registrierten universellen Link) zurück zu Ihrer App [leiten](https://docs.stripe.com/js/payment_intents/confirm_payment#confirm_payment_intent-options-confirmParams-return_url). ```javascript stripe.confirmPayment({ elements, confirmParams: { // Return URL where the customer should be redirected after the PaymentIntent is confirmed. return_url: 'https://example.com/checkout_redirect/success', }, }) .then(function(result) { if (result.error) { // Inform the customer that there was an error. } }); ``` ## Umgang mit Bestellabwicklung [Serverseitig] Wenn der/die Nutzer/in die erste Zahlung abschließt oder wenn nachfolgende wiederkehrende Zahlungen erfolgen, sendet Stripe beispielsweise folgende Ereignisse: - `invoice.payment_succeeded` - `customer.subscription.updated` - `invoice.payment_failed` Überwachen Sie diese Ereignisse in Ihrem Webhook-Endpoint. Zum Beispiel: #### Node.js ```javascript app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const sig = req.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET); } catch (err) { console.error('Webhook signature verification failed.', err.message); return res.sendStatus(400); } switch (event.type) { case 'invoice.payment_succeeded': { const invoice = event.data.object; // Mark subscription as active in your database // For example, invoice.subscription -> "sub_abc123" console.log('Payment succeeded'); break; } case 'invoice.payment_failed': { const invoice = event.data.object; console.log('Payment failed - notify the user to update their payment methods'); break; } case 'customer.subscription.updated': { const subscription = event.data.object; // For example, handle pause, cancellation, or other changes console.log(`Subscription updated: ${subscription.id}`); break; } default: console.log(`Unhandled event type ${event.type}`); } res.json({ received: true }); }); ``` Um Ihre Integration zu testen, können Sie Ereignisse im [Dashboard](https://dashboard.stripe.com/events) überwachen oder die [Stripe CLI](https://docs.stripe.com/webhooks.md#test-webhook) verwenden. Richten Sie bei der Entwicklung in der Produktion einen Webhook-Endpoint ein und abonnieren Sie die entsprechenden Ereignistypen. Wenn Sie Ihren `STRIPE_WEBHOOK_SECRET`-Schlüssel nicht kennen, klicken Sie auf den [Webhook](https://dashboard.stripe.com/webhooks) im Dashboard, um ihn anzuzeigen. ### Tests So testen Sie, ob Ihre Checkout-Schaltfläche funktioniert: 1. Klicken Sie auf die Schaltfläche zum Bezahlen, die Sie zu Ihrem Bezahlvorgang mit dem Payment Element von Stripe weiterleitet. 1. Geben Sie die Testnummer 4242 4242 4242 4242, einen dreistelligen CVC, ein Ablaufdatum und eine gültige Postleitzahl. 1. Tippen Sie auf **Bezahlen**. 1. Der Webhook `invoice.payment_succeeded` wird ausgelöst und Stripe benachrichtigt Ihren Server über die Transaktion. 1. Sie werden zu Ihrer App zurückgeleitet. Wenn Ihre Integration nicht funktioniert, sehen Sie sich die [zusätzlichen Testressourcen](https://docs.stripe.com/mobile/digital-goods/custom-checkout.md#additional-testing-resources) an. ## Optional: Zusätzliche Testressourcen Sie können mehrere Testkarten verwenden, um sicherzustellen, dass Ihre Integration bereit für den Einsatz in einer Produktionsumgebung ist. Verwenden Sie sie mit einer beliebigen Prüfziffer (CVC), einer Postleitzahl und einem Ablaufdatum in der Zukunft. | Die Nummer | Beschreibung | | ------------------- | ---------------------------------------------------------------------------------------------- | | 4242 4242 4242 4242 | Zahlung ist erfolgreich und wird sofort verarbeitet. | | 4000 0000 0000 3220 | Für eine erfolgreiche Zahlung muss die 3D Secure 2-Authentifizierung durchgeführt werden. | | 4000 0000 0000 9995 | Zahlung schlägt immer mit dem Ablehnungscode `insufficient_funds` fehl. | Eine vollständige Liste der Testkarten finden Sie im Leitfaden [Testbetrieb](https://docs.stripe.com/testing.md). ### Universelle Links testen Wenn Ihr universeller Link nicht von Ihrer Bezahlseite auf Ihre App weiterleitet, prüfen Sie die `SharedWebCredentials`-Logs auf Fehler. 1. Fügen Sie der Zugewiesenen Domain einen Debugging-Parameter hinzu. 1. Öffnen Sie den Bereich **Signierung & Kapazitäten** der Zielanwendung Ihrer App. 1. Fügen Sie dem Eintrag für Ihre zugewiesene Domain die Kennzeichnung `?mode=developer` hinzu. *(Example: `applinks:example.com?mode=developer`)* 1. Wechseln Sie in den Entwicklermodus. 1. Führen Sie eine App aus Xcode auf Ihrem Gerät aus und aktivieren Sie das Entwicklermenü. 1. Öffnen Sie auf Ihrem iPhone die **Einstellungen**, tippen Sie auf **Entwickler** und aktivieren Sie **Entwicklung Zugewiesene Domains**. 1. Löschen Sie Ihre App und installieren Sie sie neu. Dadurch wird iOS die Datei apple-app-site-association erneut herunterladen. 1. Schließen Sie den Bezahlvorgang in Ihrer App ab. 1. Checkout leitet Sie an Ihre App weiter. Falls nicht, führen Sie eine Systemdiagnose durch. 1. Drücken Sie gleichzeitig die Volumentasten (+ und -) und die Einschalttaste für 1 Sekunde, danach loslassen. Sie fühlen eine kurze Vibration, es wird aber kein visuelles Signal angezeigt. 1. Warten Sie 5 Minuten, gehen Sie dann zu **Einstellungen > Datenschutz > Analyse und Verbesserung > Analysedaten** und scrollen Sie zur letzten Systemdiagnosedatei in der Liste. 1. Tippen Sie die Taste Teilen, um per AirDrop die Datei auf Ihrem Computer zu speichern. 1. Öffnen Sie das Systemdiagnosearchiv und dann `swcutil_show.txt`. 1. Durchsuchen Sie diese Datei nach der ID Ihrer App. Sie sehen einen Bereich mit Debugging-Informationen für Ihre App, einschließlich etwaiger Fehlermeldungen. ``` Service: applinks App ID: Y28TH9SHX7.com.stripe.FruitStore App Version: 1.0 App PI: { v = 0, t = 0x8, u = 0xc98, db = E335D78F-D49E-4F19-A150-F657E50DEDAE, {length = 8, bytes = 0x980c000000000000} } Domain: example.com?mode=developer User Approval: unspecified Site/Fmwk Approval: unspecified Flags: developer Last Checked: 2021-09-23 18:16:58 +0000 Next Check: 2021-09-23 21:21:34 +0000 Error: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set. around line 1, column 0." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set. around line 1, column 0., NSJSONSerializationErrorIndex=0} Retries: 1 ``` ## See also - [Rabatte hinzufügen](https://docs.stripe.com/payments/checkout/discounts.md) - [Steuern einziehen](https://docs.stripe.com/payments/checkout/taxes.md) - [Steuer-IDs erfassen](https://docs.stripe.com/tax/checkout/tax-ids.md) - [Ihr Branding anpassen](https://docs.stripe.com/payments/checkout/customization.md) - [Bestätigungsseite anpassen](https://docs.stripe.com/payments/checkout/custom-success-page.md)