Überprüfen von Produktkäufen anhand von Belegen

Jede Microsoft Store-Transaktion, die zu einem erfolgreichen Produktkauf führt, kann optional einen Transaktionsbeleg zurückgeben. Dieser Beleg enthält Informationen über das aufgeführte Produkt und die geldpolitischen Kosten für den Kunden.

Der Zugriff auf diese Informationen unterstützt Szenarien, in denen Ihre App überprüfen muss, ob ein Benutzer Ihre App erworben hat, oder add-on (auch als In-App-Produkt oder IAP bezeichnet) im Microsoft Store erworben hat. Stellen Sie sich beispielsweise ein Spiel vor, das heruntergeladene Inhalte anbietet. Wenn der Benutzer, der den Spielinhalt erworben hat, es auf einem anderen Gerät wiedergeben möchte, müssen Sie überprüfen, ob der Benutzer bereits über den Inhalt verfügt. Gehen Sie folgendermaßen vor:

Wichtig

In diesem Artikel wird gezeigt, wie Sie Mitglieder des Windows.ApplicationModel.Store-Namespace verwenden, um einen Beleg für einen In-App-Kauf abzurufen und zu überprüfen. Wenn Sie den Windows.Services.Store-Namespace für In-App-Käufe verwenden (in Windows 10, Version 1607 eingeführt und für Projekte verfügbar, die auf Windows 10 Anniversary Edition abzielen (10.0; Build 14393) oder eine höhere Version in Visual Studio) stellt dieser Namespace keine API zum Abrufen von Kaufbestätigungen für In-App-Käufe bereit. Sie können jedoch eine REST-Methode in der Microsoft Store-Sammlungs-API verwenden, um Daten für eine Kauftransaktion abzurufen. Weitere Informationen finden Sie unter "Belege für In-App-Einkäufe".

Anfordern einer Bestätigung

Der Windows.ApplicationModel.Store-Namespace unterstützt verschiedene Möglichkeiten zum Abrufen eines Belegs:

Ein App-Beleg sieht ungefähr so aus.

Hinweis

Dieses Beispiel ist so formatiert, dass der XML-Code lesbar ist. Echte App-Bestätigungen enthalten keine Leerzeichen zwischen Elementen.

<Receipt Version="1.0" ReceiptDate="2012-08-30T23:10:05Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="4e362949-acc3-fe3a-e71b-89893eb4f528">
    <AppReceipt Id="8ffa256d-eca8-712a-7cf8-cbf5522df24b" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" PurchaseDate="2012-06-04T23:07:24Z" LicenseType="Full" />
    <ProductReceipt Id="6bbf4366-6fb2-8be8-7947-92fd5f683530" ProductId="Product1" PurchaseDate="2012-08-30T23:08:52Z" ExpirationDate="2012-09-02T23:08:49Z" ProductType="Durable" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" />
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <DigestValue>cdiU06eD8X/w1aGCHeaGCG9w/kWZ8I099rw4mmPpvdU=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>SjRIxS/2r2P6ZdgaR9bwUSa6ZItYYFpKLJZrnAa3zkMylbiWjh9oZGGng2p6/gtBHC2dSTZlLbqnysJjl7mQp/A3wKaIkzjyRXv3kxoVaSV0pkqiPt04cIfFTP0JZkE5QD/vYxiWjeyGp1dThEM2RV811sRWvmEs/hHhVxb32e8xCLtpALYx3a9lW51zRJJN0eNdPAvNoiCJlnogAoTToUQLHs72I1dECnSbeNPXiG7klpy5boKKMCZfnVXXkneWvVFtAA1h2sB7ll40LEHO4oYN6VzD+uKd76QOgGmsu9iGVyRvvmMtahvtL1/pxoxsTRedhKq6zrzCfT8qfh3C1w==</SignatureValue>
    </Signature>
</Receipt>

Ein Produktbeleg sieht wie folgt aus.

Hinweis

Dieses Beispiel ist so formatiert, dass der XML-Code lesbar ist. Echte Produktbestätigungen enthalten keine Leerzeichen zwischen Elementen.

<Receipt Version="1.0" ReceiptDate="2012-08-30T23:08:52Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="4e362949-acc3-fe3a-e71b-89893eb4f528">
    <ProductReceipt Id="6bbf4366-6fb2-8be8-7947-92fd5f683530" ProductId="Product1" PurchaseDate="2012-08-30T23:08:52Z" ExpirationDate="2012-09-02T23:08:49Z" ProductType="Durable" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" />
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <DigestValue>Uvi8jkTYd3HtpMmAMpOm94fLeqmcQ2KCrV1XmSuY1xI=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>TT5fDET1X9nBk9/yKEJAjVASKjall3gw8u9N5Uizx4/Le9RtJtv+E9XSMjrOXK/TDicidIPLBjTbcZylYZdGPkMvAIc3/1mdLMZYJc+EXG9IsE9L74LmJ0OqGH5WjGK/UexAXxVBWDtBbDI2JLOaBevYsyy+4hLOcTXDSUA4tXwPa2Bi+BRoUTdYE2mFW7ytOJNEs3jTiHrCK6JRvTyU9lGkNDMNx9loIr+mRks+BSf70KxPtE9XCpCvXyWa/Q1JaIyZI7llCH45Dn4SKFn6L/JBw8G8xSTrZ3sBYBKOnUDbSCfc8ucQX97EyivSPURvTyImmjpsXDm2LBaEgAMADg==</SignatureValue>
    </Signature>
</Receipt>

Sie können eines dieser Belegbeispiele verwenden, um den Überprüfungscode zu testen. Weitere Informationen zum Inhalt des Belegs finden Sie in den Element- und Attributbeschreibungen.

Überprüfen eines Belegs

Um die Echtheit eines Belegs zu überprüfen, benötigen Sie Ihr Back-End-System (einen Webdienst oder etwas ähnliches), um die Signatur des Belegs mithilfe des öffentlichen Zertifikats zu überprüfen. Um dieses Zertifikat abzurufen, verwenden Sie die URL https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60, wobei CertificateId der CertificateId-Wert in der Bestätigung ist.

Hier ist ein Beispiel für diesen Überprüfungsprozess. Dieser Code wird in einer .NET Framework-Konsolenanwendung ausgeführt, die einen Verweis auf die System.Security-Assembly enthält.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.IO;
using System.Security.Cryptography.Xml;
using System.Net;

namespace ReceiptVerificationSample
{
    public sealed class RSAPKCS1SHA256SignatureDescription : SignatureDescription
    {
        public RSAPKCS1SHA256SignatureDescription()
        {
            base.KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
            base.DigestAlgorithm = typeof(SHA256Managed).FullName;
            base.FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
            base.DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
        }

        public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }

            RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key);
            deformatter.SetHashAlgorithm("SHA256");
            return deformatter;
        }

        public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }

            RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
            formatter.SetHashAlgorithm("SHA256");
            return formatter;
        }
    }

    class Program
    {
        // Utility function to read the bytes from an HTTP response
        private static int ReadResponseBytes(byte[] responseBuffer, Stream resStream)
        {
            int count = 0;
            int numBytesRead = 0;
            int numBytesToRead = responseBuffer.Length;

            do
            {
                count = resStream.Read(responseBuffer, numBytesRead, numBytesToRead);
                numBytesRead += count;
                numBytesToRead -= count;
            } while (count > 0);

            return numBytesRead;
        }

        public static X509Certificate2 RetrieveCertificate(string certificateId)
        {
            const int MaxCertificateSize = 10000;

            // Retrieve the certificate URL.
            String certificateUrl = String.Format(
                "https://go.microsoft.com/fwlink/?LinkId=246509&cid={0}", certificateId);

            // Make an HTTP GET request for the certificate
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(certificateUrl);
            request.Method = "GET";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // Retrieve the certificate out of the response stream
            byte[] responseBuffer = new byte[MaxCertificateSize];
            Stream resStream = response.GetResponseStream();
            int bytesRead = ReadResponseBytes(responseBuffer, resStream);

            if (bytesRead < 1)
            {
                //TODO: Handle error here
            }

            return new X509Certificate2(responseBuffer);
        }

        static bool ValidateXml(XmlDocument receipt, X509Certificate2 certificate)
        {
            // Create the signed XML object.
            SignedXml sxml = new SignedXml(receipt);

            // Get the XML Signature node and load it into the signed XML object.
            XmlNode dsig = receipt.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0];
            if (dsig == null)
            {
                // If signature is not found return false
                System.Console.WriteLine("Signature not found.");
                return false;
            }

            sxml.LoadXml((XmlElement)dsig);

            // Check the signature
            bool isValid = sxml.CheckSignature(certificate, true);

            return isValid;
        }

        static void Main(string[] args)
        {
            // .NET does not support SHA256-RSA2048 signature verification by default, 
            // so register this algorithm for verification.
            CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), 
                "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");

            // Load the receipt that needs to be verified as an XML document
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load("..\\..\\receipt.xml");

            // The certificateId attribute is present in the document root, retrieve it
            XmlNode node = xmlDoc.DocumentElement;
            string certificateId = node.Attributes["CertificateId"].Value;

            // Retrieve the certificate from the official site.
            // NOTE: For sake of performance, you would want to cache this certificate locally.
            //       Otherwise, every single call will incur the delay of certificate retrieval.
            X509Certificate2 verificationCertificate = RetrieveCertificate(certificateId);

            try
            {
                // Validate the receipt with the certificate retrieved earlier
                bool isValid = ValidateXml(xmlDoc, verificationCertificate);
                System.Console.WriteLine("Certificate valid: " + isValid);
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.ToString());
            }
        }
    }
}

Element- und Attributbeschreibungen für einen Beleg

In diesem Abschnitt werden die Elemente und Attribute in einem Beleg beschrieben.

Receipt-Element

Das Stammelement dieser Datei ist das Receipt-Element , das Informationen zu App- und In-App-Käufen enthält. Dieses Element enthält die folgenden untergeordneten Elemente.

Element Erforderlich Menge Beschreibung
AppReceipt No 0 oder 1 Enthält Kaufinformationen für die aktuelle App.
ProductReceipt No 0 oder mehr Enthält Informationen zu einem In-App-Kauf für die aktuelle App.
Signature Ja 1 Dieses Element ist ein standardmäßiges XML-DSIG-Konstrukt. Es enthält ein SignatureValue-Element , das die Signatur enthält, mit der Sie den Beleg überprüfen können, und ein SignedInfo-Element .

Der Beleg weist die folgenden Attribute auf.

Attribute BESCHREIBUNG
Version Die Versionsnummer des Belegs.
CertificateId Der Zertifikatfingerabdruck, der zum Signieren des Belegs verwendet wird.
Belegdatum Datum, an dem die Bestätigung signiert und heruntergeladen wurde.
ReceiptDeviceId Gibt das Gerät an, das zum Anfordern dieses Belegs verwendet wird.

AppReceipt-Element

Dieses Element enthält Kaufinformationen für die aktuelle App.

AppReceipt hat die folgenden Attribute.

Attribute Beschreibung
Id Identifiziert den Kauf.
AppId Der Paketfamilienname-Wert, den das Betriebssystem für die App verwendet.
LicenseType Vollständig, wenn der Benutzer die Vollversion der App erworben hat. Testversion, wenn der Benutzer eine Testversion der App heruntergeladen hat.
PurchaseDate Datum, an dem die App erworben wurde.

ProductReceipt-Element

Dieses Element enthält Informationen zu einem In-App-Kauf für die aktuelle App.

ProductReceipt hat die folgenden Attribute.

Attribute Beschreibung
Id Identifiziert den Kauf.
AppId Gibt die App an, über die der Benutzer den Kauf getätigt hat.
ProductId Identifiziert das gekaufte Produkt.
ProductType Bestimmt den Produkttyp. Unterstützt derzeit nur den Wert " Durable".
PurchaseDate Datum, an dem der Kauf erfolgte.