Menggunakan tanda terima untuk memverifikasi pembelian produk

Setiap transaksi Microsoft Store yang menghasilkan pembelian produk yang berhasil dapat secara opsional mengembalikan tanda terima transaksi. Tanda terima ini memberikan informasi tentang produk yang tercantum dan biaya moneter kepada pelanggan.

Memiliki akses ke informasi ini mendukung skenario di mana aplikasi Anda perlu memverifikasi bahwa pengguna membeli aplikasi Anda, atau telah melakukan pembelian add-on (juga disebut produk dalam aplikasi atau IAP) dari Microsoft Store. Misalnya, bayangkan game yang menawarkan konten yang diunduh. Jika pengguna yang membeli konten game ingin memutarnya di perangkat lain, Anda perlu memverifikasi bahwa pengguna sudah memiliki konten. Berikut caranya.

Penting

Artikel ini memperlihatkan cara menggunakan anggota namespace Windows.ApplicationModel.Store untuk mendapatkan dan memvalidasi tanda terima untuk pembelian dalam aplikasi. Jika Anda menggunakan namespace Windows.Services.Store untuk pembelian dalam aplikasi (diperkenalkan di Windows 10, versi 1607, dan tersedia untuk proyek yang menargetkan Windows 10 Anniversary Edition (10.0; Build 14393) atau rilis yang lebih baru di Visual Studio), namespace layanan ini tidak menyediakan API untuk mendapatkan tanda terima pembelian untuk pembelian dalam aplikasi. Namun, Anda dapat menggunakan metode REST di API pengumpulan Microsoft Store untuk mendapatkan data untuk transaksi pembelian. Untuk informasi selengkapnya, lihat Tanda terima untuk pembelian dalam aplikasi.

Meminta tanda terima

Namespace Windows.ApplicationModel.Store mendukung beberapa cara untuk mendapatkan tanda terima:

Tanda terima aplikasi terlihat seperti ini.

Catatan

Contoh ini diformat untuk membantu membuat XML dapat dibaca. Tanda terima aplikasi nyata tidak menyertakan spasi kosong di antara elemen.

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

Tanda terima produk terlihat seperti ini.

Catatan

Contoh ini diformat untuk membantu membuat XML dapat dibaca. Tanda terima produk nyata tidak termasuk spasi kosong di antara elemen.

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

Anda dapat menggunakan salah satu contoh tanda terima ini untuk menguji kode validasi Anda. Untuk informasi selengkapnya tentang konten tanda terima, lihat deskripsi elemen dan atribut.

Memvalidasi tanda terima

Untuk memvalidasi keaslian tanda terima, Anda memerlukan sistem back-end Anda (layanan web atau yang serupa) untuk memeriksa tanda tangan tanda terima menggunakan sertifikat publik. Untuk mendapatkan sertifikat ini, gunakan URL https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60, di mana CertificateId adalah nilai CertificateId dalam tanda terima.

Berikut adalah contoh proses validasi tersebut. Kode ini berjalan dalam aplikasi konsol .NET Framework yang menyertakan referensi ke rakitan 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());
            }
        }
    }
}

Deskripsi elemen dan atribut untuk tanda terima

Bagian ini menjelaskan elemen dan atribut dalam tanda terima.

Elemen Tanda Terima

Elemen akar file ini adalah elemen Tanda Terima , yang berisi informasi tentang pembelian aplikasi dan dalam aplikasi. Elemen ini berisi elemen anak berikut.

Elemen Diperlukan Kuantitas Deskripsi
AppReceipt Tidak 0 atau 1 Berisi informasi pembelian untuk aplikasi saat ini.
ProductReceipt Tidak 0 atau lebih Berisi informasi tentang pembelian dalam aplikasi untuk aplikasi saat ini.
Tanda Tangan Ya 1 Elemen ini adalah konstruksi XML-DSIG standar. Ini berisi elemen SignatureValue , yang berisi tanda tangan yang dapat Anda gunakan untuk memvalidasi tanda terima, dan elemen SignedInfo .

Tanda terima memiliki atribut berikut.

Atribut Deskripsi
Versi Nomor versi tanda terima.
CertificateId Thumbprint sertifikat yang digunakan untuk menandatangani tanda terima.
Tanda TerimaDate Tanggal tanda terima ditandatangani dan diunduh.
ReceiptDeviceId Mengidentifikasi perangkat yang digunakan untuk meminta tanda terima ini.

Elemen AppReceipt

Elemen ini berisi informasi pembelian untuk aplikasi saat ini.

AppReceipt memiliki atribut berikut.

Atribut Deskripsi
Id Mengidentifikasi pembelian.
AppId Nilai Nama Keluarga Paket yang digunakan OS untuk aplikasi.
Jenis Lisensi Penuh, jika pengguna membeli versi lengkap aplikasi. Uji coba, jika pengguna mengunduh versi percobaan aplikasi.
PurchaseDate Tanggal saat aplikasi diperoleh.

Elemen ProductReceipt

Elemen ini berisi informasi tentang pembelian dalam aplikasi untuk aplikasi saat ini.

ProductReceipt memiliki atribut berikut.

Atribut Deskripsi
Id Mengidentifikasi pembelian.
AppId Mengidentifikasi aplikasi tempat pengguna melakukan pembelian.
ProductId Mengidentifikasi produk yang dibeli.
ProductType Menentukan jenis produk. Saat ini hanya mendukung nilai Durable.
PurchaseDate Tanggal pembelian terjadi.