Поделиться через


Использование квитанций для проверки покупок продуктов

Каждая транзакция Microsoft Store, которая приводит к успешной покупке продукта, может при необходимости возвращать квитанцию о транзакциях. Эта квитанция содержит сведения о перечисленных продуктах и денежных затратах для клиента.

Доступ к этой информации поддерживает сценарии, в которых приложение должно убедиться, что пользователь приобрел приложение или сделал покупки надстройки (также называемые продуктами в приложении или IAP) из Microsoft Store. Например, представьте игру, которая предлагает скачанный контент. Если пользователь, приобретший игровое содержимое, хочет воспроизвести его на другом устройстве, необходимо убедиться, что пользователь уже владеет содержимым. Это делается следующим образом.

Внимание

В этой статье показано, как использовать элементы пространства имен Windows.ApplicationModel.Store для получения и проверки квитанции для покупки в приложении. Если вы используете пространство имен Windows.Services.Store для покупок в приложении (представлено в Windows 10 версии 1607 и доступно для проектов, предназначенных для Юбилейного выпуска Windows 10 (10.0; Сборка 14393) или более поздней версии в Visual Studio), это пространство имен не предоставляет API для получения квитанций о покупке в приложении. Однако можно использовать метод REST в API сбора Microsoft Store для получения данных для транзакции покупки. Дополнительные сведения см. в разделе "Квитанции" для покупок в приложении.

Запрос квитанции

Пространство имен Windows.ApplicationModel.Store поддерживает несколько способов получения квитанции:

Получение приложения выглядит примерно так.

Примечание.

Этот пример отформатирован, чтобы помочь сделать XML-файл доступным для чтения. Реальные квитанции приложений не включают пробелы между элементами.

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

Квитанция о продукте выглядит следующим образом.

Примечание.

Этот пример отформатирован, чтобы помочь сделать XML-файл доступным для чтения. Реальные квитанции о продукте не включают пробелы между элементами.

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

Для проверки кода проверки можно использовать любой из этих примеров квитанций. Дополнительные сведения о содержимом квитанции см . в описаниях элементов и атрибутов.

Проверка квитанции

Чтобы проверить подлинность квитанции, вам потребуется серверная система (веб-служба или что-то подобное), чтобы проверить подпись квитанции с помощью общедоступного сертификата. Чтобы получить этот сертификат, используйте URL-адрес https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60, где CertificateId значение CertificateId в квитанции.

Ниже приведен пример этого процесса проверки. Этот код выполняется в консольном приложении платформа .NET Framework, которое содержит ссылку на сборку 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());
            }
        }
    }
}

Описания элементов и атрибутов для квитанции

В этом разделе описываются элементы и атрибуты в квитанции.

Элемент Receipt

Корневой элемент этого файла — элемент Receipt , содержащий сведения о покупках приложений и в приложении. Этот элемент содержит следующие дочерние элементы.

Элемент Обязательное поле Количество Description
AppReceipt No 0 или 1 Содержит сведения о покупке для текущего приложения.
ProductReceipt No 0 и более Содержит сведения о покупке в приложении для текущего приложения.
Подпись Да 1 Этот элемент является стандартной конструкцией XML-DSIG. Он содержит элемент SignatureValue , содержащий подпись, которую можно использовать для проверки квитанции и элемента SignedInfo .

Получение имеет следующие атрибуты.

Атрибут Description
Версия Номер версии квитанции.
CertificateId Отпечаток сертификата, используемый для подписи квитанции.
Получение Дата подписания и скачивания квитанции.
ReceiptDeviceId Определяет устройство, используемое для запроса этой квитанции.

Элемент AppReceipt

Этот элемент содержит сведения о покупке для текущего приложения.

AppReceipt имеет следующие атрибуты.

Атрибут Описание
Id Определяет покупку.
AppId Значение семейства пакетов, которое использует ос для приложения.
LicenseType Полный, если пользователь приобрел полную версию приложения. Пробная версия, если пользователь скачал пробную версию приложения.
PurchaseDate Дата приобретения приложения.

Элемент ProductReceipt

Этот элемент содержит сведения о покупке в приложении для текущего приложения.

ProductReceipt имеет следующие атрибуты.

Атрибут Описание
Id Определяет покупку.
AppId Определяет приложение, через которое пользователь сделал покупку.
ProductId Определяет приобретенный продукт.
ProductType Определяет тип продукта. В настоящее время поддерживается только значение устойчивых.
PurchaseDate Дата возникновения покупки.