次の方法で共有


レシートを使用して製品の購入を確認する

製品の購入が成功する各 Microsoft Store トランザクションは、必要に応じてトランザクションの領収書を返すことができます。 この領収書は、リストされている製品と金額に関する情報を顧客に提供します。

この情報にアクセスすることは、ユーザーがアプリを購入したことをアプリで確認する必要があるシナリオや、Microsoft Store からアドオン (アプリ内製品または IAP とも呼ばれます) を購入したシナリオをサポートします。 たとえば、ダウンロードしたコンテンツを提供するゲームを想像してみてください。 ゲーム コンテンツを購入したユーザーが別のデバイスでゲーム コンテンツを再生する場合は、ユーザーが既にコンテンツを所有していることを確認する必要があります。 その方法を次に示します。

Von Bedeutung

この記事では、 Windows.ApplicationModel.Store 名前空間のメンバーを使用して、アプリ内購入のレシートを取得して検証する方法について説明します。 Windows.Services.Store 名前空間をアプリ内購入に使用している場合 (Windows 10 バージョン 1607 で導入 され、Windows 10 Anniversary Edition (10.0;ビルド 14393) 以降の Visual Studio のリリース)、この名前空間には、アプリ内購入の購入確認書を取得するための API は用意されていません。 ただし、Microsoft Store コレクション API の REST メソッドを使用して、購入トランザクションのデータを取得できます。 詳細については、「 アプリ内購入の領収書」を参照してください。

領収書の要求

Windows.ApplicationModel.Store 名前空間では、レシートを取得するいくつかの方法がサポートされています。

  • CurrentApp.RequestAppPurchaseAsync または CurrentApp.RequestProductPurchaseAsync またはこのメソッドの他のオーバーロードのいずれかを使用して購入する際、戻り値にはレシートが含まれます。
  • CurrentApp.GetAppReceiptAsync メソッドを呼び出して、アプリとアプリ内のアドオンの現在のレシート情報を取得できます。

アプリの領収書は次のようになります。

この例は、XML を読みやすくするために書式設定されています。 実際のアプリのレシートには、要素間の空白は含まれません。

<Receipt Version="1.0" ReceiptDate="2012-08-30T23:10:05Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="0a0a0a0a-1111-bbbb-2222-3c3c3c3c3c3c">
    <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="0a0a0a0a-1111-bbbb-2222-3c3c3c3c3c3c">
    <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>

これらのレシートの例のいずれかを使用して、検証コードをテストできます。 レシートの内容の詳細については、 要素と属性の説明を参照してください。

レシートの検証

レシートの信頼性を検証するには、パブリック証明書を使用してレシートの署名を確認するバックエンド システム (Web サービスなど) が必要です。 この証明書を取得するには、URL https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60を使用します。ここで、 CertificateId はレシートの CertificateId 値です。

その検証プロセスの例を次に示します。 このコードは、 System.Security アセンブリへの参照を含む .NET Framework コンソール アプリケーションで実行されます。

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 要素です。 この要素には、次の子要素が含まれています。

要素 必須 数量 説明
AppReceipt いいえ 0 または 1 現在のアプリの購入情報が含まれています。
製品受領書 いいえ 0 以上 現在のアプリのアプリ内購入に関する情報が含まれています。
署名 イエス 1 この要素は、標準 のXML-DSIG コンストラクトです。 これには SignatureValue 要素が含まれています。この要素には、レシートの検証に使用できる署名と SignedInfo 要素が含まれています。

Receipt には次の属性があります。

特性 説明
バージョン レシートのバージョン番号。
CertificateId の レシートの署名に使用される証明書の拇印。
受取日 領収書に署名してダウンロードした日付。
領収書デバイスID この領収書を要求するために使用されるデバイスを識別します。

AppReceipt 要素

この要素には、現在のアプリの購入情報が含まれています。

AppReceipt には、次の属性があります。

特性 説明
身分証明書 購入を識別します。
AppId OS がアプリに使用するパッケージ ファミリ名の値。
ライセンスタイプ ユーザーがアプリのフル バージョンを購入した場合は完全。 試用版 (ユーザーがアプリの試用版をダウンロードした場合)。
購入日 アプリが取得された日付。

ProductReceipt 要素

この要素には、現在のアプリのアプリ内購入に関する情報が含まれています。

ProductReceipt には、次の属性があります。

特性 説明
身分証明書 購入を識別します。
AppId ユーザーが購入を行ったアプリを識別します。
ProductId 購入した製品を識別します。
製品タイプ 製品の種類を決定します。 現在サポートされているのは、Durableの値のみです。
購入日 購入が発生した日付。