Condividi tramite


Usare le ricevute per verificare gli acquisti di prodotti

Ogni transazione di Microsoft Store che genera un acquisto di prodotto riuscito può facoltativamente restituire una ricevuta di transazione. Questa ricevuta fornisce informazioni sul prodotto elencato e sul costo monetario per il cliente.

Avere accesso a queste informazioni supporta scenari in cui l'app deve verificare che un utente abbia acquistato l'app o abbia effettuato acquisti di componenti aggiuntivi (detti anche prodotti in-app o IAP) da Microsoft Store. Si supponga, ad esempio, di un gioco che offre contenuti scaricati. Se l'utente che ha acquistato il contenuto del gioco vuole riprodurlo in un dispositivo diverso, è necessario verificare che l'utente sia già proprietario del contenuto. Ecco come.

Importante

Questo articolo illustra come usare i membri dello spazio dei nomi Windows.ApplicationModel.Store per ottenere e convalidare una ricevuta per un acquisto in-app. Se usi lo spazio dei nomi Windows.Services.Store per gli acquisti in-app (introdotto in Windows 10 versione 1607 e disponibile per i progetti destinati a Windows 10 Anniversary Edition (10.0; Build 14393) o una versione successiva in Visual Studio), questo spazio dei nomi non fornisce un'API per ottenere ricevute di acquisto per gli acquisti in-app. Tuttavia, è possibile usare un metodo REST nell'API di raccolta di Microsoft Store per ottenere i dati per una transazione di acquisto. Per altre informazioni, vedere Ricevute per gli acquisti in-app.

Richiesta di una ricevuta

Lo spazio dei nomi Windows.ApplicationModel.Store supporta diversi modi per ottenere una ricevuta:

Una ricevuta dell'app ha un aspetto simile al seguente.

Nota

Questo esempio viene formattato per rendere leggibile il codice XML. Le ricevute delle app reali non includono spazi vuoti tra gli elementi.

<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>

Una ricevuta del prodotto è simile alla seguente.

Nota

Questo esempio viene formattato per rendere leggibile il codice XML. Le ricevute dei prodotti reali non includono spazi vuoti tra gli elementi.

<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>

È possibile usare uno di questi esempi di ricevuta per testare il codice di convalida. Per altre informazioni sul contenuto della ricevuta, vedere l'elemento e le descrizioni degli attributi.

Convalida di una ricevuta

Per convalidare l'autenticità di una ricevuta, è necessario il sistema back-end (un servizio Web o qualcosa di simile) per controllare la firma della ricevuta usando il certificato pubblico. Per ottenere questo certificato, usare l'URL https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60, dove CertificateId è il valore CertificateId nella ricevuta.

Ecco un esempio del processo di convalida. Questo codice viene eseguito in un'applicazione console .NET Framework che include un riferimento al gruppo System.Security.

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());
            }
        }
    }
}

Descrizioni di elementi e attributi per una ricevuta

In questa sezione vengono descritti gli elementi e gli attributi in una ricevuta.

Elemento ricevuta

L'elemento radice di questo file è l'elemento Ricevuta , che contiene informazioni sugli acquisti di app e in-app. Questo elemento contiene i seguenti elementi figlio:

Elemento Richiesto Quantità Descrizione
AppReceipt No 0 o 1 Contiene informazioni sull'acquisto per l'app corrente.
ProductReceipt No 0 o più personalizzati Contiene informazioni su un acquisto in-app per l'app corrente.
Firma 1 Questo elemento è un costrutto XML-DSIG standard. Contiene un elemento SignatureValue che contiene la firma che è possibile usare per convalidare la ricevuta e un elemento SignedInfo.

La Ricevuta dispone dei seguenti attributi.

Attributo Descrizione
Versione Numero di versione della ricevuta.
CertificateId Identificazione personale del certificato utilizzata per firmare la ricevuta.
ReceiptDate Data di firma e download della ricevuta.
ReceiptDeviceId Identifica il dispositivo usato per richiedere la ricevuta.

Elemento AppReceipt

Questo elemento contiene informazioni sull'acquisto per l'app corrente.

AppReceipt dispone dei seguenti attributi.

Attributo Descrizione
Id Identifica l'acquisto.
AppId Valore Package Family Name usato dal sistema operativo per l'app.
LicenseType Full, se l'utente ha acquistato la versione completa dell'app. Trial, se l'utente ha scaricato una versione di prova dell'app.
PurchaseDate Data di acquisizione dell'app.

Elemento ProductReceipt

Questo elemento contiene informazioni su un acquisto in-app per l'app corrente.

ProductReceipt ha gli attributi seguenti.

Attributo Descrizione
Id Identifica l'acquisto.
AppId Identifica l'app tramite cui l'utente ha effettuato l'acquisto.
ProductId Identifica il prodotto acquistato.
ProductType Determina il tipo di prodotto. Attualmente supporta solo un valore Durable.
PurchaseDate Data in cui si è verificato l'acquisto.